/* * 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.core.internal.trading; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.bind.ValidationEvent; import javax.xml.bind.ValidationEventHandler; import javax.xml.namespace.QName; import javax.xml.transform.stream.StreamSource; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.Status; import org.eclipsetrader.core.feed.IPricingListener; import org.eclipsetrader.core.feed.IQuote; import org.eclipsetrader.core.feed.ITrade; import org.eclipsetrader.core.feed.PricingDelta; import org.eclipsetrader.core.feed.PricingEvent; import org.eclipsetrader.core.instruments.ISecurity; import org.eclipsetrader.core.internal.CoreActivator; import org.eclipsetrader.core.markets.IMarketService; import org.eclipsetrader.core.markets.MarketPricingEnvironment; import org.eclipsetrader.core.trading.AlertEvent; import org.eclipsetrader.core.trading.IAlert; import org.eclipsetrader.core.trading.IAlertListener; import org.eclipsetrader.core.trading.IAlertService; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; public class AlertService implements IAlertService { MarketPricingEnvironment pricingEnvironment; Map<ISecurity, List<IAlert>> map = new HashMap<ISecurity, List<IAlert>>(); Map<ISecurity, List<IAlert>> triggeredMap = new HashMap<ISecurity, List<IAlert>>(); ListenerList listeners = new ListenerList(ListenerList.IDENTITY); private IPricingListener pricingListener = new IPricingListener() { @Override public void pricingUpdate(PricingEvent event) { doPricingUpdate(event); } }; public AlertService() { } public void startUp() throws Exception { BundleContext context = CoreActivator.getDefault().getBundle().getBundleContext(); ServiceReference serviceReference = context.getServiceReference(IMarketService.class.getName()); pricingEnvironment = new MarketPricingEnvironment((IMarketService) context.getService(serviceReference)); context.ungetService(serviceReference); load(CoreActivator.getDefault().getStateLocation().append("alerts.xml").toFile()); for (ISecurity instrument : map.keySet()) { pricingEnvironment.addSecurity(instrument); ITrade trade = pricingEnvironment.getTrade(instrument); IQuote quote = pricingEnvironment.getQuote(instrument); for (IAlert alert : map.get(instrument)) { alert.setInitialValues(trade, quote); } } pricingEnvironment.addPricingListener(pricingListener); } void load(File file) throws JAXBException { if (!file.exists()) { return; } JAXBContext jaxbContext = JAXBContext.newInstance(InstrumentElement[].class); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); unmarshaller.setEventHandler(new ValidationEventHandler() { @Override public boolean handleEvent(ValidationEvent event) { Status status = new Status(IStatus.WARNING, CoreActivator.PLUGIN_ID, 0, "Error validating XML: " + event.getMessage(), null); //$NON-NLS-1$ CoreActivator.log(status); return true; } }); JAXBElement<InstrumentElement[]> element = unmarshaller.unmarshal(new StreamSource(file), InstrumentElement[].class); if (element == null) { return; } for (InstrumentElement ie : element.getValue()) { ISecurity instrument = ie.getInstrument(); List<IAlert> list = new ArrayList<IAlert>(); AlertElement[] alerts = ie.getAlerts(); for (int ii = 0; ii < alerts.length; ii++) { Map<String, Object> parameters = new HashMap<String, Object>(); for (ParameterElement param : alerts[ii].getParameters()) { parameters.put(param.getName(), ParameterElement.convert(param)); } IAlert alert = alerts[ii].getAlert(); if (alert != null) { alert.setParameters(parameters); list.add(alert); } } map.put(instrument, list); } } protected synchronized void doPricingUpdate(PricingEvent event) { List<IAlert> set = map.get(event.getSecurity()); if (set == null) { return; } List<IAlert> list = new ArrayList<IAlert>(); List<IAlert> triggeredList; synchronized (triggeredMap) { triggeredList = triggeredMap.get(event.getSecurity()); if (triggeredList == null) { triggeredList = new ArrayList<IAlert>(); triggeredMap.put(event.getSecurity(), triggeredList); } } for (IAlert alert : set) { if (triggeredList.contains(alert)) { continue; } for (PricingDelta delta : event.getDelta()) { if (delta.getNewValue() instanceof ITrade) { alert.setTrade((ITrade) delta.getNewValue()); if (alert.isTriggered()) { triggeredList.add(alert); list.add(alert); break; } } if (delta.getNewValue() instanceof IQuote) { alert.setQuote((IQuote) delta.getNewValue()); if (alert.isTriggered()) { triggeredList.add(alert); list.add(alert); break; } } } } if (list.size() != 0) { ITrade trade = pricingEnvironment.getTrade(event.getSecurity()); IQuote quote = pricingEnvironment.getQuote(event.getSecurity()); AlertEvent alertEvent = new AlertEvent(event.getSecurity(), trade, quote, list.toArray(new IAlert[list.size()])); fireAlertTriggeredEvent(alertEvent); } } protected void fireAlertTriggeredEvent(AlertEvent alertEvent) { Object[] l = listeners.getListeners(); for (int i = 0; i < l.length; i++) { try { ((IAlertListener) l[i]).alertTriggered(alertEvent); } catch (Throwable t) { Status status = new Status(IStatus.ERROR, CoreActivator.PLUGIN_ID, 0, "Error notifying listeners", t); //$NON-NLS-1$ CoreActivator.log(status); } } } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IAlertService#addAlertListener(org.eclipsetrader.core.trading.IAlertListener) */ @Override public void addAlertListener(org.eclipsetrader.core.trading.IAlertListener l) { listeners.add(l); } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IAlertService#removeAlertListener(org.eclipsetrader.core.trading.IAlertListener) */ @Override public void removeAlertListener(org.eclipsetrader.core.trading.IAlertListener l) { listeners.remove(l); } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IAlertService#resetTrigger(org.eclipsetrader.core.trading.IAlert) */ @Override public void resetTrigger(IAlert alert) { for (List<IAlert> triggeredList : triggeredMap.values()) { triggeredList.remove(alert); } } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IAlertService#resetAllTriggers() */ @Override public void resetAllTriggers() { triggeredMap.clear(); } public void shutDown() throws IllegalStateException, JAXBException, IOException { pricingEnvironment.dispose(); listeners.clear(); List<InstrumentElement> list = new ArrayList<InstrumentElement>(); for (ISecurity instrument : map.keySet()) { List<AlertElement> alertList = new ArrayList<AlertElement>(); for (IAlert alert : map.get(instrument)) { alertList.add(new AlertElement(alert)); } list.add(new InstrumentElement(instrument, alertList)); } save(CoreActivator.getDefault().getStateLocation().append("alerts.xml").toFile(), list.toArray(new InstrumentElement[list.size()])); } void save(File file, InstrumentElement[] elements) throws JAXBException, IOException { if (file.exists()) { file.delete(); } JAXBContext jaxbContext = JAXBContext.newInstance(InstrumentElement[].class); Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setEventHandler(new ValidationEventHandler() { @Override public boolean handleEvent(ValidationEvent event) { Status status = new Status(IStatus.WARNING, CoreActivator.PLUGIN_ID, 0, "Error validating XML: " + event.getMessage(), null); //$NON-NLS-1$ CoreActivator.log(status); return true; } }); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); marshaller.setProperty(Marshaller.JAXB_ENCODING, System.getProperty("file.encoding")); //$NON-NLS-1$ JAXBElement<InstrumentElement[]> element = new JAXBElement<InstrumentElement[]>(new QName("list"), InstrumentElement[].class, elements); marshaller.marshal(element, new FileWriter(file)); } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IAlertService#getAlerts(org.eclipsetrader.core.instruments.ISecurity) */ @Override public IAlert[] getAlerts(ISecurity instrument) { List<IAlert> list = map.get(instrument); if (list == null) { return new IAlert[0]; } return list.toArray(new IAlert[list.size()]); } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IAlertService#setAlerts(org.eclipsetrader.core.instruments.ISecurity, org.eclipsetrader.core.trading.IAlert[]) */ @Override public void setAlerts(ISecurity instrument, IAlert[] alerts) { if (!map.containsKey(instrument)) { pricingEnvironment.addSecurity(instrument); } List<IAlert> oldList = map.get(instrument); if (oldList != null) { ITrade trade = pricingEnvironment.getTrade(instrument); IQuote quote = pricingEnvironment.getQuote(instrument); for (int i = 0; i < alerts.length; i++) { if (!oldList.contains(alerts[i])) { alerts[i].setInitialValues(trade, quote); } } } map.put(instrument, new ArrayList<IAlert>(Arrays.asList(alerts))); } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IAlertService#getTriggeredAlerts(org.eclipsetrader.core.instruments.ISecurity) */ @Override public IAlert[] getTriggeredAlerts(ISecurity instrument) { List<IAlert> list = triggeredMap.get(instrument); if (list == null) { return new IAlert[0]; } return list.toArray(new IAlert[list.size()]); } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IAlertService#hasTriggeredAlerts(org.eclipsetrader.core.instruments.ISecurity) */ @Override public boolean hasTriggeredAlerts(ISecurity instrument) { List<IAlert> list = triggeredMap.get(instrument); if (list == null) { return false; } return !list.isEmpty(); } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IAlertService#resetTriggers(org.eclipsetrader.core.instruments.ISecurity) */ @Override public void resetTriggers(ISecurity instrument) { ITrade trade = pricingEnvironment.getTrade(instrument); IQuote quote = pricingEnvironment.getQuote(instrument); synchronized (triggeredMap) { for (IAlert alert : triggeredMap.get(instrument)) { alert.setInitialValues(trade, quote); } triggeredMap.remove(instrument); } fireAlertTriggeredEvent(new AlertEvent(instrument, trade, quote, new IAlert[0])); } }