/* * The MtGox-Java API is free software: you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The MtGox-Java API 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 * Lesser GNU General Public License for more details. * * You should have received a copy of the Lesser GNU General Public License * along with the MtGox-Java API . If not, see <http://www.gnu.org/licenses/>. */ package to.sparks.mtgox.service; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.databind.ObjectMapper; import io.socket.SocketIO; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.json.JSONObject; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.context.ApplicationListener; import org.springframework.core.task.SimpleAsyncTaskExecutor; import to.sparks.mtgox.MtGoxWebsocketClient; import to.sparks.mtgox.event.DepthEvent; import to.sparks.mtgox.event.PacketEvent; import to.sparks.mtgox.event.TickerEvent; import to.sparks.mtgox.event.TradeEvent; import to.sparks.mtgox.model.*; /** * * @author SparksG */ class WebsocketClientService implements Runnable, MtGoxWebsocketClient, ApplicationEventPublisherAware, ApplicationListener<PacketEvent> { private final static String SOCKET_IO_SERVER = "http://socketio.mtgox.com/mtgox";// "http://socketio-beta.mtgox.com/mtgox"; // "http://socketio.mtgox.com/mtgox"; private ApplicationEventPublisher applicationEventPublisher = null; private Logger logger; // private BaseWebSocketClient websocket; SocketIO socket; private SimpleAsyncTaskExecutor taskExecutor; private Map<String, CurrencyInfo> currencyCache; private HTTPClientV1Service httpAPIV1; private SocketListener socketListener; // private ReliabilityOptions reliability; public WebsocketClientService(Logger logger, SimpleAsyncTaskExecutor taskExecutor, HTTPClientV1Service httpAPIV1, SocketListener socketListener) { this(logger, taskExecutor, httpAPIV1, socketListener, true); } public WebsocketClientService(Logger logger, SimpleAsyncTaskExecutor taskExecutor, HTTPClientV1Service httpAPIV1, SocketListener socketListener, boolean autoRestartSocket) { this.logger = logger; this.taskExecutor = taskExecutor; this.httpAPIV1 = httpAPIV1; currencyCache = new HashMap<>(); currencyCache.put("BTC", CurrencyInfo.BitcoinCurrencyInfo); this.socketListener = socketListener; // reliability = new ReliabilityOptions(autoRestartSocket, 10000L, 30000L, Integer.MAX_VALUE, Integer.MAX_VALUE); // websocket = new BaseWebSocketClient(reliability); } public void init() { taskExecutor.execute(this); } public void destroy() { try { if (socket != null) { socket.disconnect(); } } catch (Exception ex) { logger.log(Level.SEVERE, null, ex); } finally { socket = null; } } @Override public void shutdown() { // taskExecutor.shutdown(); } /* * Close and reopen the websocket. Use a Spring scheduler to call this every * 15 minutes or so. */ public void recycleWebsocketConnection() { // try { logger.info("Recycle websocket."); destroy(); // // socket = new SocketIO(SOCKET_IO_SERVER); // socket.connect(socketListener); init(); // } catch (MalformedURLException ex) { // logger.log(Level.SEVERE, null, ex); // } } private CurrencyInfo getCachedCurrencyInfo(String currencyCode) { CurrencyInfo ci = null; if (!currencyCache.containsKey(currencyCode)) { try { ci = httpAPIV1.getCurrencyInfo(currencyCode); currencyCache.put(currencyCode, ci); } catch (Exception ex) { logger.log(Level.SEVERE, null, ex); } } ci = currencyCache.get(currencyCode); return ci; } public void tradeEvent(Trade trade) { if (applicationEventPublisher != null) { CurrencyInfo ci = getCachedCurrencyInfo(trade.getPrice_currency()); if (ci != null) { trade.setCurrencyInfo(ci); } TradeEvent event = new TradeEvent(this, trade); applicationEventPublisher.publishEvent(event); } } public void tickerEvent(Ticker ticker) { if (applicationEventPublisher != null) { CurrencyInfo ci = getCachedCurrencyInfo(ticker.getCurrencyCode()); if (ci != null) { ticker.setCurrencyInfo(ci); } TickerEvent event = new TickerEvent(this, ticker); applicationEventPublisher.publishEvent(event); } } public void depthEvent(Depth depth) { if (applicationEventPublisher != null) { DepthEvent event = new DepthEvent(this, depth); applicationEventPublisher.publishEvent(event); } } @Override public void run() { try { //socket = new SocketIO("http://socketio-beta.mtgox.com/mtgox"); socket = new SocketIO(SOCKET_IO_SERVER); socket.connect(socketListener); // websocket.addListener(socketListener); // websocket.open("ws://socketio-beta.mtgox.com"); logger.info("WebSocket API Client started."); } catch (Exception ex) { logger.log(Level.SEVERE, null, ex); } } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } @Override public void onApplicationEvent(PacketEvent event) { JSONObject op = (JSONObject) event.getPayload(); try { // logger.fine(aPacket.getUTF8()); JsonFactory factory = new JsonFactory(); ObjectMapper mapper = new ObjectMapper(); // JsonParser jp = factory.createJsonParser(aPacket.getUTF8()); // DynaBean op = mapper.readValue(jp, DynaBean.class); if (op.get("op") != null && op.get("op").equals("private")) { String messageType = op.get("private").toString(); if (messageType.equalsIgnoreCase("ticker")) { OpPrivateTicker opPrivateTicker = mapper.readValue(factory.createJsonParser(op.toString()), OpPrivateTicker.class); Ticker ticker = opPrivateTicker.getTicker(); tickerEvent(ticker); logger.log(Level.FINE, "Ticker: last: {0}", new Object[]{ticker.getLast().toPlainString()}); } else if (messageType.equalsIgnoreCase("depth")) { OpPrivateDepth opPrivateDepth = mapper.readValue(factory.createJsonParser(op.toString()), OpPrivateDepth.class); Depth depth = opPrivateDepth.getDepth(); depthEvent(depth); logger.log(Level.FINE, "Depth total volume: {0}", new Object[]{depth.getTotalVolume().toPlainString()}); } else if (messageType.equalsIgnoreCase("trade")) { OpPrivateTrade opPrivateTrade = mapper.readValue(factory.createJsonParser(op.toString()), OpPrivateTrade.class); Trade trade = opPrivateTrade.getTrade(); tradeEvent(trade); logger.log(Level.FINE, "Trade currency: {0}", new Object[]{trade.getPrice_currency()}); } else { logger.log(Level.WARNING, "Unknown private operation: {0}", new Object[]{op.toString()}); } // logger.log(Level.INFO, "messageType: {0}, payload: {1}", new Object[]{messageType, dataPayload}); } else { logger.log(Level.WARNING, "Unknown operation: {0}, payload: {1}", new Object[]{op.get("op"), op.toString()}); // TODO: Process the following types // subscribe // unsubscribe // remark // result } } catch (Exception ex) { logger.log(Level.SEVERE, null, ex); } } }