package org.ripple.power.ui;
import java.io.IOException;
import java.util.ArrayList;
import java.util.StringTokenizer;
import org.json.JSONObject;
import org.ripple.power.config.LSystem;
import org.ripple.power.config.Session;
import org.ripple.power.i18n.LangConfig;
import org.ripple.power.qr.WebRippled;
import org.ripple.power.txns.CurrencyUtils;
import org.ripple.power.txns.Updateable;
import org.ripple.power.utils.HttpRequest;
import org.ripple.power.utils.HttpRequest.HttpRequestException;
import org.ripple.power.utils.MathUtils;
import org.ripple.power.utils.StringUtils;
import org.ripple.power.wallet.WalletItem;
import com.ripple.client.Client;
import com.ripple.client.enums.Command;
import com.ripple.client.requests.Request;
import com.ripple.client.responses.Response;
import com.ripple.client.transport.impl.JavaWebSocketTransportImpl;
import com.ripple.core.coretypes.AccountID;
import com.ripple.core.coretypes.RippleDate;
public class RPClient {
static {
System.setProperty("https.protocols", "TLSv1.2");
}
// Test status is not networking
public static boolean testing = false;
private static RPClient _rippleClient = null;
private final ArrayList<Updateable> _loads = new ArrayList<Updateable>(10);
private final ArrayList<Updateable> _longloads = new ArrayList<Updateable>(
10);
private final static String[] applicationPublicNodes = new String[] {
"wss://s1.ripple.com:51233", "wss://s2.ripple.com:51233",
"wss://s-west.ripple.com:51233", "wss://s-east.ripple.com:51233",
"wss://s1.ripple.com:443", "wss://s2.ripple.com:443",
"wss://s-west.ripple.com:443", "wss://s-east.ripple.com:443",
"wss://r.ripple.com:443", "wss://ripplenode.io:5006" };
public static ArrayList<String> getRLNodes(boolean flag) {
ArrayList<String> tmp = new ArrayList<String>(40);
tmp.add("wss://localhost:443");
for (String rippled : applicationPublicNodes) {
tmp.add(rippled);
}
if (flag) {
try {
ArrayList<String> list = RPClient.loadRLNodes();
if (list != null && list.size() > 0) {
tmp.addAll(list);
}
} catch (Exception ex) {
}
}
return tmp;
}
public static void saveRippledNode(String wss) {
if (wss == null) {
return;
}
Session session = LSystem.session("ripple_node");
session.set("data", wss.trim());
session.save();
}
public static String getRippledNode() {
Session session = LSystem.session("ripple_node");
String result = session.get("data");
if (result != null && result.startsWith("wss://")
&& result.length() > 6) {
return result;
} else {
return applicationPublicNodes[MathUtils.random(0,
applicationPublicNodes.length - 1)];
}
}
public static ArrayList<String> loadRLNodes() throws HttpRequestException,
IOException {
ArrayList<String> list = new ArrayList<String>(30);
WebRippled rippled = loadWebRippledConfig("https://ripple.com/ripple.txt");
for (String ips : rippled.ips) {
String result = "wss://" + StringUtils.split(ips, " ")[0] + ":443";
list.add(result);
}
return list;
}
public static WebRippled loadWebRippledConfig(String url)
throws HttpRequestException, IOException {
WebRippled rippled = new WebRippled();
// 突然发现报错Could not generate DH
// keypair(最早是没有的,不知道rl又换了什么……),似乎遇到java的bug了,目前验证ripple.com站的ssl证书会溢出,然后报错的是sun.security.ssl.SSLSocketImpl部分,私有代码想修改都不行,只能过几天换成openssl直接读吧……
HttpRequest request = HttpRequest.get(url);
request.trustAllHosts();
try {
request.trustAllCerts();
} catch (Exception ex) {
}
boolean ok = false;
try {
ok = request.ok();
} catch (Throwable ex) {
}
String result = null;
if (ok) {
result = request.body();
} else {
// 亲测除了java外(而且android下也正常),其它用什么都解析正常,等着oracle打补丁吧,暂时先用c#写个小程序读取下……
try {
result = HttpRequest.fix_ssl_open(url);
} catch (Exception e) {
return rippled;
}
}
if (result.indexOf('\r') != -1) {
result = StringUtils.replace(result, "\r", "\n");
}
StringTokenizer str = new StringTokenizer(result, "\n");
int flagNext = -1;
for (; str.hasMoreTokens();) {
String context = str.nextToken().trim();
if (context != null && context.length() > 0) {
if (context.startsWith("[")) {
if ("[accounts]".equals(context)) {
flagNext = 0;
} else if ("[validation_public_key]".equals(context)) {
flagNext = 1;
} else if ("[domain]".equals(context)) {
flagNext = 2;
} else if ("[ips]".equals(context)) {
flagNext = 3;
} else if ("[validators]".equals(context)) {
flagNext = 4;
} else if ("[authinfo_url]".equals(context)) {
flagNext = 5;
} else {
flagNext = -1;
}
continue;
}
switch (flagNext) {
case 0:
rippled.accounts.add(context);
break;
case 1:
rippled.validation_public_key.add(context);
break;
case 2:
rippled.domain.add(context);
break;
case 3:
rippled.ips.add(context);
break;
case 4:
rippled.validators.add(context);
break;
case 5:
rippled.authinfo_url.add(context);
break;
default:
break;
}
}
}
return rippled;
}
//
public void addLongLoad(Updateable u) {
synchronized (_longloads) {
_longloads.add(u);
}
}
public void removeLongLoad(Updateable u) {
synchronized (_longloads) {
_longloads.remove(u);
}
}
public void removeAllLongLoad() {
synchronized (_longloads) {
_longloads.clear();
}
}
public void loadLong() {
final int count = _longloads.size();
if (count > 0) {
callLongUpdateable(_longloads);
}
}
private final static void callLongUpdateable(
final ArrayList<Updateable> list) {
synchronized (list) {
for (int i = 0; i < list.size(); i++) {
Updateable running = list.get(i);
synchronized (running) {
running.action(null);
}
}
}
}
// Updateable Region
public void addLoad(Updateable u) {
synchronized (_loads) {
_loads.add(u);
}
}
public void removeLoad(Updateable u) {
synchronized (_loads) {
_loads.remove(u);
}
}
public void removeAllLoad() {
synchronized (_loads) {
_loads.clear();
}
}
public void load() {
final int count = _loads.size();
if (count > 0) {
callUpdateable(_loads);
}
}
private final static void callUpdateable(final ArrayList<Updateable> list) {
ArrayList<Updateable> loadCache;
synchronized (list) {
loadCache = new ArrayList<Updateable>(list);
list.clear();
}
for (int i = 0; i < loadCache.size(); i++) {
Updateable running = loadCache.get(i);
synchronized (running) {
running.action(null);
}
}
loadCache = null;
}
public static RPClient ripple() {
if (_rippleClient == null) {
synchronized (RPClient.class) {
_rippleClient = new RPClient();
if (!testing) {
_rippleClient.threadLoop();
} else {
MainForm form = LSystem.applicationMain;
if (form != null) {
MainPanel panel = form.getMainPanel();
if (panel != null) {
panel.setSpeedIcon("empty");
}
}
}
}
}
return _rippleClient;
}
public static void stop() {
if (_rippleClient != null) {
synchronized (RPClient.class) {
if (!testing) {
_rippleClient.threadStop();
_rippleClient = null;
}
}
}
}
public synchronized static void reset() {
stop();
ripple();
}
private final Client pClinet;
private Thread pThread;
private String node_path = "unkown";
private boolean pLooping;
public String getBaseFee() {
Client client = getClinet();
if (client != null) {
if (client.serverInfo.fee_ref == 0
|| client.serverInfo.load_base == 0) {
return "0.01";
}
float fee_unit = client.serverInfo.fee_base
/ client.serverInfo.fee_ref;
fee_unit *= client.serverInfo.load_factor
/ client.serverInfo.load_base;
fee_unit *= 10f;
return CurrencyUtils.getRippleToValue(String.valueOf(fee_unit));
}
return "Unkown";
}
public synchronized void threadStop() {
if (pThread != null) {
this.pLooping = false;
try {
pThread.interrupt();
pThread = null;
} catch (Throwable t) {
}
}
}
public synchronized void threadLoop() {
if (pThread == null) {
this.pLooping = true;
Runnable runnable = new Runnable() {
@Override
public void run() {
for (; pLooping;) {
Client client = getClinet();
if (client != null) {
MainForm form = LSystem.applicationMain;
if (form != null) {
MainPanel panel = form.getMainPanel();
if (panel != null) {
if (client.serverInfo.server_status != null) {
panel.walletChanged(client.serverInfo.server_status);
} else {
panel.walletChanged("none");
}
if (client.getSpeed() <= 100) {
panel.setSpeedIcon("all");
} else if (client.getSpeed() <= 1000) {
panel.setSpeedIcon("lv5");
} else if (client.getSpeed() <= 1500) {
panel.setSpeedIcon("lv4");
} else if (client.getSpeed() <= 2500) {
panel.setSpeedIcon("lv3");
} else if (client.getSpeed() <= 3500) {
panel.setSpeedIcon("lv2");
} else if (client.getSpeed() <= 4500) {
panel.setSpeedIcon("lv1");
} else {
panel.setSpeedIcon("lv0");
}
}
}
}
load();
loadLong();
final long sleep = LSystem.applicationSleep;
if (sleep > 0) {
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
}
} else {
Thread.yield();
}
}
}
};
pThread = new Thread(runnable);
pThread.start();
}
}
public RPClient() {
// ClientLogger.quiet = true;
pClinet = new Client(new JavaWebSocketTransportImpl());
if (LSystem.applicationProxy != null) {
pClinet.setProxy(LSystem.applicationProxy.getProxy());
}
if (!testing) {
node_path = getRippledNode();
pClinet.connect(node_path);
}
}
public String getNodePath() {
return node_path;
}
public AccountID getAccountID(String address) {
AccountID id = AccountID.fromAddress(address);
return id;
}
public Request newRequest(Command command) {
return pClinet.newRequest(command);
}
public void xrp(final WalletItem item) {
try {
Request req = pClinet.newRequest(Command.account_info);
req.json("account", item.getPublicKey());
req.once(Request.OnSuccess.class, new Request.OnSuccess() {
@Override
public void called(Response response) {
JSONObject arrays = response.result;
JSONObject result = arrays.getJSONObject("account_data");
String number = CurrencyUtils.getRippleToValue(result
.getString("Balance"));
if (item.isTip()) {
double new_amount = Double.parseDouble(number);
double old_amount = Double.parseDouble(item.getAmount());
if (old_amount > new_amount) {
popXRP(item.getPublicKey(), LangConfig.get(
RPClient.class, "lower", "Lower"),
old_amount - new_amount);
} else if (new_amount > old_amount) {
popXRP(item.getPublicKey(), LangConfig.get(
RPClient.class, "heighten", "Heighten"),
new_amount - old_amount);
}
}
item.setAmount(number);
item.setStatus("full");
}
});
req.once(Request.OnError.class, new Request.OnError() {
@Override
public void called(Response response) {
item.setStatus("none");
}
});
req.request();
} catch (Exception ex) {
// null
}
}
private final static String time() {
return LangConfig.get(RPClient.class, "in", "In")
+ RippleDate.now().getTimeString() + ", "
+ LangConfig.get(RPClient.class, "ya", "Your address") + ": ";
}
private static void popXRP(String address, String flag, double amount) {
RPBubbleDialog.pop(time() + address + flag + " "
+ LSystem.getNumberShort(amount) + LSystem.nativeCurrency);
}
public Client getClinet() {
return pClinet;
}
}