/* * Copyright (c) 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.directaworld.internal.core.connector; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URI; import java.net.URISyntaxException; import java.text.NumberFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.TimeZone; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.core.net.proxy.IProxyData; import org.eclipse.core.net.proxy.IProxyService; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExecutableExtension; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.Status; import org.eclipse.equinox.security.storage.ISecurePreferences; import org.eclipse.equinox.security.storage.SecurePreferencesFactory; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.window.Window; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.PlatformUI; import org.eclipsetrader.core.feed.IConnectorListener; import org.eclipsetrader.core.feed.IFeedConnector; import org.eclipsetrader.core.feed.IFeedIdentifier; import org.eclipsetrader.core.feed.IFeedSubscription; import org.eclipsetrader.core.feed.LastClose; import org.eclipsetrader.core.feed.Quote; import org.eclipsetrader.core.feed.TodayOHL; import org.eclipsetrader.core.feed.Trade; import org.eclipsetrader.directaworld.internal.Activator; import org.eclipsetrader.directaworld.internal.core.repository.IdentifierType; import org.eclipsetrader.directaworld.internal.core.repository.IdentifiersList; import org.eclipsetrader.directaworld.internal.core.repository.PriceDataType; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; public class SnapshotConnector implements Runnable, IFeedConnector, IExecutableExtension { private static final int I_SYMBOL = 0; //private static final int I_NAME = 1; private static final int I_LAST = 2; //private static final int I_CHANGE = 3; private static final int I_VOLUME = 4; private static final int I_TIME = 5; private static final int I_DATE = 6; private static final int I_BID = 7; private static final int I_BID_SIZE = 8; private static final int I_ASK = 9; private static final int I_ASK_SIZE = 10; private static final int I_OPEN = 12; private static final int I_CLOSE = 13; private static final int I_LOW = 14; private static final int I_HIGH = 15; private static final String HOST = "registrazioni.directaworld.it"; //$NON-NLS-1$ private String id; private String name; protected Map<String, FeedSubscription> symbolSubscriptions; private ListenerList listeners = new ListenerList(ListenerList.IDENTITY); protected TimeZone timeZone; private SimpleDateFormat dateTimeParser; private SimpleDateFormat timeParser; private NumberFormat numberFormat; private HttpClient client; private String userName; private String password; private Thread thread; private boolean connected; private boolean stopping; private int requiredDelay = 15; private Log logger = LogFactory.getLog(getClass()); public SnapshotConnector() { symbolSubscriptions = new HashMap<String, FeedSubscription>(); timeZone = TimeZone.getTimeZone("Europe/Rome"); //$NON-NLS-1$ dateTimeParser = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); //$NON-NLS-1$ dateTimeParser.setTimeZone(timeZone); timeParser = new SimpleDateFormat("HH:mm:ss"); //$NON-NLS-1$ timeParser.setTimeZone(timeZone); numberFormat = NumberFormat.getInstance(Locale.ITALY); } /* (non-Javadoc) * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object) */ @Override public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException { id = config.getAttribute("id"); //$NON-NLS-1$ name = config.getAttribute("name"); //$NON-NLS-1$ } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IFeedConnector#getId() */ @Override public String getId() { return id; } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IFeedConnector#getName() */ @Override public String getName() { return name; } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IFeedConnector#subscribe(org.eclipsetrader.core.feed.IFeedIdentifier) */ @Override public IFeedSubscription subscribe(IFeedIdentifier identifier) { synchronized (symbolSubscriptions) { IdentifierType identifierType = IdentifiersList.getInstance().getIdentifierFor(identifier); FeedSubscription subscription = symbolSubscriptions.get(identifierType.getSymbol()); if (subscription == null) { subscription = new FeedSubscription(this, identifierType); symbolSubscriptions.put(identifierType.getSymbol(), subscription); if (connected) { startThread(); } } subscription.incrementInstanceCount(); return subscription; } } protected void disposeSubscription(FeedSubscription subscription) { synchronized (symbolSubscriptions) { if (subscription.decrementInstanceCount() <= 0) { IdentifierType identifierType = subscription.getIdentifierType(); symbolSubscriptions.remove(identifierType.getSymbol()); if (symbolSubscriptions.size() == 0 && connected) { stopThread(); } } } } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IFeedConnector#connect() */ @Override public synchronized void connect() { connected = true; synchronized (symbolSubscriptions) { if (symbolSubscriptions.size() != 0) { startThread(); } } } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IFeedConnector#disconnect() */ @Override public synchronized void disconnect() { stopThread(); connected = false; } protected void startThread() { if (thread == null) { stopping = false; thread = new Thread(this, name + " - Data Reader"); //$NON-NLS-1$ thread.start(); } } protected void stopThread() { stopping = true; if (thread != null) { try { synchronized (thread) { thread.notify(); } if (thread != null) { thread.join(30 * 1000); } } catch (InterruptedException e) { Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, "Error stopping thread", e); //$NON-NLS-1$ Activator.log(status); } thread = null; } } public boolean isRunning() { return thread != null; } /* (non-Javadoc) * @see java.lang.Runnable#run() */ @Override public void run() { final IPreferenceStore preferenceStore = Activator.getDefault().getPreferenceStore(); final ISecurePreferences securePreferences; if (preferenceStore.getBoolean(Activator.PREFS_USE_SECURE_PREFERENCE_STORE)) { securePreferences = SecurePreferencesFactory.getDefault().node(Activator.PLUGIN_ID); try { if (userName == null) { userName = securePreferences.get(Activator.PREFS_USERNAME, ""); //$NON-NLS-1$ } if (password == null) { password = securePreferences.get(Activator.PREFS_PASSWORD, ""); //$NON-NLS-1$ } } catch (Exception e) { final Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Error accessing secure storage", e); //$NON-NLS-1$ Display.getDefault().syncExec(new Runnable() { @Override public void run() { Activator.log(status); ErrorDialog.openError(null, null, null, status); } }); } } else { securePreferences = null; if (userName == null) { userName = preferenceStore.getString(Activator.PREFS_USERNAME); } if (password == null) { password = preferenceStore.getString(Activator.PREFS_PASSWORD); } } client = new HttpClient(); client.getHttpConnectionManager().getParams().setConnectionTimeout(30000); try { setupProxy(client, HOST); } catch (URISyntaxException e) { final Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Error setting proxy", e); //$NON-NLS-1$ Display.getDefault().syncExec(new Runnable() { @Override public void run() { Activator.log(status); ErrorDialog.openError(null, null, null, status); } }); } do { if (userName == null || password == null || "".equals(userName) || "".equals(password)) { //$NON-NLS-1$ //$NON-NLS-2$ Display.getDefault().syncExec(new Runnable() { @Override public void run() { Shell shell = PlatformUI.isWorkbenchRunning() ? PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell() : null; LoginDialog dlg = new LoginDialog(shell, userName, password); if (dlg.open() == Window.OK) { userName = dlg.getUserName(); password = dlg.getPassword(); if (dlg.isSavePassword()) { if (preferenceStore.getBoolean(Activator.PREFS_USE_SECURE_PREFERENCE_STORE)) { try { securePreferences.put(Activator.PREFS_USERNAME, userName, true); securePreferences.put(Activator.PREFS_PASSWORD, dlg.isSavePassword() ? password : "", true); //$NON-NLS-1$ } catch (Exception e) { Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Error accessing secure storage", e); //$NON-NLS-1$ Activator.log(status); ErrorDialog.openError(null, null, null, status); } } else { preferenceStore.putValue(Activator.PREFS_USERNAME, userName); preferenceStore.putValue(Activator.PREFS_PASSWORD, dlg.isSavePassword() ? password : ""); //$NON-NLS-1$ } } } else { userName = null; password = null; } } }); if (userName == null || password == null) { client = null; thread = null; return; } } } while (!checkLogin()); synchronized (thread) { while (!stopping) { if (symbolSubscriptions.size() != 0) { fetchLatestSnapshot(); } try { thread.wait(requiredDelay * 1000); } catch (InterruptedException e) { // Ignore exception, not important at this time } } } client = null; thread = null; } private void setupProxy(HttpClient client, String host) throws URISyntaxException { if (Activator.getDefault() != null) { BundleContext context = Activator.getDefault().getBundle().getBundleContext(); ServiceReference<IProxyService> reference = context.getServiceReference(IProxyService.class); if (reference != null) { IProxyService proxyService = context.getService(reference); IProxyData[] proxyData = proxyService.select(new URI(null, host, null, null)); for (int i = 0; i < proxyData.length; i++) { if (IProxyData.HTTP_PROXY_TYPE.equals(proxyData[i].getType())) { IProxyData data = proxyData[i]; if (data.getHost() != null) { client.getHostConfiguration().setProxy(data.getHost(), data.getPort()); } if (data.isRequiresAuthentication()) { client.getState().setProxyCredentials(AuthScope.ANY, new UsernamePasswordCredentials(data.getUserId(), data.getPassword())); } break; } } context.ungetService(reference); } } } protected IPreferenceStore getPreferenceStore() { return Activator.getDefault().getPreferenceStore(); } protected void fetchLatestSnapshot() { BufferedReader in = null; try { String[] symbols; synchronized (symbolSubscriptions) { symbols = symbolSubscriptions.keySet().toArray(new String[symbolSubscriptions.size()]); } StringBuilder url = new StringBuilder("http://" + HOST + "/cgi-bin/qta?idx=alfa&modo=t&appear=n"); //$NON-NLS-1$ //$NON-NLS-2$ int x = 0; for (; x < symbols.length; x++) { url.append("&id" + (x + 1) + "=" + symbols[x]); //$NON-NLS-1$ //$NON-NLS-2$ } for (; x < 30; x++) { url.append("&id" + (x + 1) + "="); //$NON-NLS-1$ //$NON-NLS-2$ } url.append("&u=" + userName + "&p=" + password); //$NON-NLS-1$ //$NON-NLS-2$ HttpMethod method = new GetMethod(url.toString()); method.setFollowRedirects(true); logger.debug(method.getURI().toString()); client.executeMethod(method); requiredDelay = 15; String inputLine; in = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream())); while ((inputLine = in.readLine()) != null) { logger.debug(inputLine); if (inputLine.indexOf("<!--QT START HERE-->") != -1) { //$NON-NLS-1$ while ((inputLine = in.readLine()) != null) { logger.debug(inputLine); if (inputLine.indexOf("<!--QT STOP HERE-->") != -1) { //$NON-NLS-1$ break; } try { parseLine(inputLine); } catch (Exception e) { Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, "Error parsing line: " + inputLine, e); //$NON-NLS-1$ Activator.log(status); } } } else if (inputLine.indexOf("Sara' possibile ricaricare la pagina tra") != -1) { //$NON-NLS-1$ int beginIndex = inputLine.indexOf("tra ") + 4; //$NON-NLS-1$ int endIndex = inputLine.indexOf("sec") - 1; //$NON-NLS-1$ try { requiredDelay = Integer.parseInt(inputLine.substring(beginIndex, endIndex)) + 1; } catch (Exception e) { Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, "Error parsing required delay", e); //$NON-NLS-1$ Activator.log(status); } } } in.close(); FeedSubscription[] subscriptions; synchronized (symbolSubscriptions) { Collection<FeedSubscription> c = symbolSubscriptions.values(); subscriptions = c.toArray(new FeedSubscription[c.size()]); } for (int i = 0; i < subscriptions.length; i++) { subscriptions[i].fireNotification(); } } catch (Exception e) { Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, "Error reading data", e); //$NON-NLS-1$ Activator.log(status); } finally { try { if (in != null) { in.close(); } } catch (Exception e) { // We can't do anything at this time, ignore } } } protected void parseLine(String line) throws ParseException { String[] item = line.split(";"); //$NON-NLS-1$ FeedSubscription subscription = symbolSubscriptions.get(item[I_SYMBOL]); if (subscription != null) { IdentifierType identifierType = subscription.getIdentifierType(); PriceDataType priceData = identifierType.getPriceData(); try { if (item[I_TIME].length() == 7) { item[I_TIME] = item[I_TIME].charAt(0) + ":" + item[I_TIME].charAt(1) + item[I_TIME].charAt(3) + ":" + item[5].charAt(4) + item[I_TIME].charAt(6); //$NON-NLS-1$ //$NON-NLS-2$ } if ("".equals(item[I_DATE]) || " ".equals(item[I_DATE]) || " 0/ /0".equals(item[I_DATE])) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ priceData.setTime(timeParser.parse(item[I_TIME])); } else { priceData.setTime(dateTimeParser.parse(item[I_DATE] + " " + item[I_TIME])); //$NON-NLS-1$ } } catch (Exception e) { Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, "Error parsing date in line: " + line, e); //$NON-NLS-1$ Activator.log(status); } priceData.setLast(numberFormat.parse(item[I_LAST]).doubleValue()); priceData.setVolume(numberFormat.parse(item[I_VOLUME]).longValue()); subscription.setTrade(new Trade(priceData.getTime(), priceData.getLast(), null, priceData.getVolume())); priceData.setBid(numberFormat.parse(item[I_BID]).doubleValue()); priceData.setBidSize(numberFormat.parse(item[I_BID_SIZE]).longValue()); priceData.setAsk(numberFormat.parse(item[I_ASK]).doubleValue()); priceData.setAskSize(numberFormat.parse(item[I_ASK_SIZE]).longValue()); subscription.setQuote(new Quote(priceData.getBid(), priceData.getAsk(), priceData.getBidSize(), priceData.getAskSize())); priceData.setOpen(numberFormat.parse(item[I_OPEN]).doubleValue()); priceData.setHigh(numberFormat.parse(item[I_HIGH]).doubleValue()); priceData.setLow(numberFormat.parse(item[I_LOW]).doubleValue()); if (priceData.getOpen() != 0.0 && priceData.getHigh() != 0.0 && priceData.getLow() != 0.0) { subscription.setTodayOHL(new TodayOHL(priceData.getOpen(), priceData.getHigh(), priceData.getLow())); } priceData.setClose(numberFormat.parse(item[I_CLOSE]).doubleValue()); subscription.setLastClose(new LastClose(priceData.getClose(), null)); } } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IFeedConnector#addConnectorListener(org.eclipsetrader.core.feed.IConnectorListener) */ @Override public void addConnectorListener(IConnectorListener listener) { listeners.add(listener); } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IFeedConnector#removeConnectorListener(org.eclipsetrader.core.feed.IConnectorListener) */ @Override public void removeConnectorListener(IConnectorListener listener) { listeners.remove(listener); } protected boolean checkLogin() { boolean result = false; BufferedReader in = null; try { StringBuilder url = new StringBuilder("http://" + HOST + "/cgi-bin/qta?idx=alfa&modo=t&appear=n"); //$NON-NLS-1$ //$NON-NLS-2$ int x = 0; for (; x < 30; x++) { url.append("&id" + (x + 1) + "="); //$NON-NLS-1$ //$NON-NLS-2$ } url.append("&u=" + userName + "&p=" + password); //$NON-NLS-1$ //$NON-NLS-2$ HttpMethod method = new GetMethod(url.toString()); method.setFollowRedirects(true); client.executeMethod(method); requiredDelay = 15; String inputLine; in = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream())); while ((inputLine = in.readLine()) != null) { if (inputLine.indexOf("<!--QT START HERE-->") != -1) { //$NON-NLS-1$ result = true; } } in.close(); } catch (Exception e) { Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, "Error reading data", e); //$NON-NLS-1$ Activator.log(status); } finally { try { if (in != null) { in.close(); } } catch (Exception e) { // We can't do anything at this time, ignore } } return result; } }