/* * Copyright 2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.bearchoke.platform.server.frontend.web.websocket.service.impl; import com.bearchoke.platform.server.frontend.web.websocket.service.Quote; import com.bearchoke.platform.server.frontend.web.websocket.service.QuoteService; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.messaging.core.MessageSendingOperations; import org.springframework.messaging.simp.broker.BrokerAvailabilityEvent; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.math.MathContext; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.atomic.AtomicBoolean; /** * Services that push data to web socket channels need to be under the 'web' package as that is where the web socket configuration * is located. If it is below 'web', it won't find the messagingTemplate. */ @Log4j2 @Service public class QuoteServiceImpl implements ApplicationListener<BrokerAvailabilityEvent>, QuoteService { private final MessageSendingOperations<String> messagingTemplate; private final StockQuoteGenerator quoteGenerator = new StockQuoteGenerator(); private AtomicBoolean brokerAvailable = new AtomicBoolean(); @Autowired public QuoteServiceImpl(MessageSendingOperations<String> messagingTemplate) { this.messagingTemplate = messagingTemplate; } @Override public void onApplicationEvent(BrokerAvailabilityEvent event) { this.brokerAvailable.set(event.isBrokerAvailable()); } @Scheduled(fixedDelay=2000) public void sendQuotes() { for (Quote quote : this.quoteGenerator.generateQuotes()) { if (log.isTraceEnabled()) { log.trace("Sending quote " + quote); } if (this.brokerAvailable.get()) { this.messagingTemplate.convertAndSend("/topic/price.stock." + quote.getTicker(), quote); } } } private static class StockQuoteGenerator { private static final MathContext mathContext = new MathContext(2); private final Random random = new Random(); private final List<Quote> quotes = new ArrayList<>(8); public StockQuoteGenerator() { quotes.add(new Quote("Citrix Systems, Inc.", "CTXS", 24.30)); quotes.add(new Quote("Dell Inc.", "DELL", 13.44)); quotes.add(new Quote("Microsoft", "MSFT", 34.15)); quotes.add(new Quote("Oracle", "ORCL", 31.22)); quotes.add(new Quote("Google", "GOOG", 39.31)); quotes.add(new Quote("EMC Corp", "EMC", 45.01)); quotes.add(new Quote("VMWare Inc.", "VMW", 95.11)); quotes.add(new Quote("Red Hat Inc.", "RHT", 58.30)); } public List<Quote> generateQuotes() { for (int i = 0; i < quotes.size(); i++) { Quote quote = quotes.get(i); quote.setPrice(getNewPrice(quote.getPrice())); } return quotes; } private double getNewPrice(double currentPrice) { BigDecimal seedPrice = new BigDecimal(currentPrice, mathContext); double range = seedPrice.multiply(new BigDecimal(0.02)).doubleValue(); BigDecimal priceChange = new BigDecimal(String.valueOf(this.random.nextDouble() * range), mathContext); return seedPrice.add(priceChange).doubleValue(); } } }