/* * Copyright (calendar) 2004-2011 Marco Maccaferri and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Marco Maccaferri - initial API and implementation */ package org.eclipsetrader.yahoo.internal.core.connector; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipsetrader.core.feed.Quote; import org.eclipsetrader.core.feed.TodayOHL; import org.eclipsetrader.core.feed.Trade; import org.eclipsetrader.yahoo.internal.YahooActivator; import org.eclipsetrader.yahoo.internal.core.Util; import org.eclipsetrader.yahoo.internal.core.repository.IdentifierType; import org.eclipsetrader.yahoo.internal.core.repository.PriceDataType; public class StreamingConnector extends SnapshotConnector { public static final String K_SYMBOL = "s"; public static final String K_LAST = "l10"; public static final String K_VOLUME = "v00"; public static final String K_ASK_PRICE = "a00"; public static final String K_ASK_SIZE = "a50"; public static final String K_BID_PRICE = "b00"; public static final String K_BID_SIZE = "b60"; public static final String K_HIGH = "h00"; public static final String K_LOW = "g00"; public static final String K_TIME = "t10"; private static StreamingConnector instance; private StringBuilder line; private StringBuilder script; private boolean inTag; private boolean inScript; public StreamingConnector() { } public synchronized static StreamingConnector getInstance() { if (instance == null) { instance = new StreamingConnector(); } return instance; } /* (non-Javadoc) * @see org.eclipsetrader.yahoo.internal.feed.SnapshotMarketFeed#run() */ @Override public void run() { BufferedReader in = null; char[] buffer = new char[512]; try { HttpClient client = new HttpClient(new MultiThreadedHttpConnectionManager()); client.getHttpConnectionManager().getParams().setConnectionTimeout(5000); Util.setupProxy(client, Util.streamingFeedHost); HttpMethod method = null; while (!isStopping()) { // Check if the connection was not yet initialized or there are changed in the subscriptions. if (in == null || isSubscriptionsChanged()) { try { if (method != null) { method.releaseConnection(); } if (in != null) { in.close(); } } catch (Exception e) { // We can't do anything at this time, ignore } String[] symbols; synchronized (symbolSubscriptions) { Set<String> s = new HashSet<String>(symbolSubscriptions.keySet()); s.add("MSFT"); symbols = s.toArray(new String[s.size()]); setSubscriptionsChanged(false); if (symbols.length == 0) { break; } } method = Util.getStreamingFeedMethod(symbols); client.executeMethod(method); in = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream())); line = new StringBuilder(); script = new StringBuilder(); inTag = false; inScript = false; fetchLatestSnapshot(client, symbols, false); } if (in.ready()) { int length = in.read(buffer); if (length == -1) { in.close(); in = null; continue; } processIncomingChars(buffer, length); } else { // Check stale data List<String> updateList = new ArrayList<String>(); synchronized (symbolSubscriptions) { long currentTime = System.currentTimeMillis(); for (FeedSubscription subscription : symbolSubscriptions.values()) { long elapsedTime = currentTime - subscription.getIdentifierType().getLastUpdate(); if (elapsedTime >= 60000) { updateList.add(subscription.getIdentifierType().getSymbol()); subscription.getIdentifierType().setLastUpdate(currentTime / 60000 * 60000); } } } if (updateList.size() != 0) { fetchLatestSnapshot(client, updateList.toArray(new String[updateList.size()]), true); } } Thread.sleep(100); } } catch (Exception e) { Status status = new Status(IStatus.ERROR, YahooActivator.PLUGIN_ID, 0, "Error reading data", e); YahooActivator.log(status); } finally { try { if (in != null) { in.close(); } } catch (Exception e) { // We can't do anything at this time, ignore } } } protected void processIncomingChars(char[] chars, int length) { for (int i = 0; i < length; i++) { char ch = chars[i]; if (ch == '<' && !inTag) { inTag = true; } if (inTag) { line.append(ch); } if (inScript) { script.append(ch); } if (ch == '>' && inTag) { inTag = false; String tag = line.toString(); if (tag.equals("<script>")) { inScript = true; } if (tag.equals("</script>")) { inScript = false; if (script.length() >= tag.length()) { script.delete(script.length() - tag.length(), script.length()); } Map<String, String> valueMap = parseScript(script.toString()); processValues(valueMap); script = new StringBuilder(); } line = new StringBuilder(); } } } protected void processValues(Map<String, String> valueMap) { String symbol = valueMap.get(K_SYMBOL); FeedSubscription subscription = symbolSubscriptions.get(symbol); if (subscription != null) { IdentifierType identifierType = subscription.getIdentifierType(); PriceDataType priceData = identifierType.getPriceData(); if (valueMap.containsKey(K_TIME)) { priceData.setTime(new Date(getLongValue(valueMap.get(K_TIME)).longValue() * 1000)); } long tradeSize = 0; if (valueMap.containsKey(K_VOLUME)) { tradeSize = getLongValue(valueMap.get(K_VOLUME)) - (priceData.getVolume() != null ? priceData.getVolume() : 0); priceData.setLastSize(tradeSize); } if (valueMap.containsKey(K_LAST)) { priceData.setLast(getDoubleValue(valueMap.get(K_LAST))); } subscription.setTrade(new Trade(priceData.getTime(), priceData.getLast(), priceData.getLastSize(), priceData.getVolume())); if (valueMap.containsKey(K_BID_PRICE)) { priceData.setBid(getDoubleValue(valueMap.get(K_BID_PRICE))); } if (valueMap.containsKey(K_BID_SIZE)) { priceData.setBidSize(getLongValue(valueMap.get(K_BID_SIZE))); } if (valueMap.containsKey(K_ASK_PRICE)) { priceData.setAsk(getDoubleValue(valueMap.get(K_ASK_PRICE))); } if (valueMap.containsKey(K_ASK_SIZE)) { priceData.setAskSize(getLongValue(valueMap.get(K_ASK_SIZE))); } subscription.setQuote(new Quote(priceData.getBid(), priceData.getAsk(), priceData.getBidSize(), priceData.getAskSize())); if (valueMap.containsKey(K_HIGH)) { priceData.setHigh(getDoubleValue(valueMap.get(K_HIGH))); } if (valueMap.containsKey(K_LOW)) { priceData.setLow(getDoubleValue(valueMap.get(K_LOW))); } if (valueMap.containsKey(K_VOLUME)) { priceData.setVolume(getLongValue(valueMap.get(K_VOLUME))); } if (priceData.getOpen() != null && priceData.getOpen() != 0.0 && priceData.getHigh() != null && priceData.getHigh() != 0.0 && priceData.getLow() != null && priceData.getLow() != 0.0) { subscription.setTodayOHL(new TodayOHL(priceData.getOpen(), priceData.getHigh(), priceData.getLow())); } subscription.fireNotification(); } } protected Map<String, String> parseScript(String script) { Map<String, String> map = new HashMap<String, String>(); int e = 0; int s = script.indexOf("unixtime"); if (s != -1) { s += 10; e = script.indexOf(',', s); if (e == -1) { e = script.indexOf('}', s); } map.put("unixtime", script.substring(s, e)); } s = script.indexOf("open"); if (s != -1) { s += 6; e = script.indexOf(',', s); if (e == -1) { e = script.indexOf('}', s); } map.put("open", script.substring(s, e)); } s = script.indexOf("close"); if (s != -1) { s += 7; e = script.indexOf(',', s); if (e == -1) { e = script.indexOf('}', s); } map.put("close", script.substring(s, e)); } s = script.indexOf('"', e); if (s != -1) { s++; e = script.indexOf('"', s); String symbol = script.substring(s, e); map.put(K_SYMBOL, symbol); boolean inExpression = false; boolean inValue = false; int vs = -1; int ve = -1; for (int i = e + 1; i < script.length(); i++) { char ch = script.charAt(i); if (inExpression) { if (ch == ':') { e = i; } if (ch == '"') { inValue = !inValue; if (inValue) { vs = i + 1; } else { ve = i; try { String key = script.substring(s, e); String value = script.substring(vs, ve); map.put(key, value); } catch (RuntimeException e1) { System.err.println(script); e1.printStackTrace(); } } } if ((ch == ',' || ch == '}') && !inValue) { inExpression = false; } } else { if (Character.isLetter(ch)) { inExpression = true; s = i; } } } } return map; } }