1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package async; 18 19 import java.text.DecimalFormat; 20 import java.util.List; 21 import java.util.Random; 22 import java.util.concurrent.CopyOnWriteArrayList; 23 import java.util.concurrent.atomic.AtomicInteger; 24 25 public class Stockticker implements Runnable { 26 public volatile boolean run = true; 27 protected final AtomicInteger counter = new AtomicInteger(0); 28 final List<TickListener> listeners = new CopyOnWriteArrayList<>(); 29 protected volatile Thread ticker = null; 30 protected volatile int ticknr = 0; 31 start()32 public synchronized void start() { 33 run = true; 34 ticker = new Thread(this); 35 ticker.setName("Ticker Thread"); 36 ticker.start(); 37 } 38 stop()39 public synchronized void stop() { 40 // On context stop this can be called multiple times. 41 // NO-OP is the ticker thread is not set 42 // (i.e. stop() has already completed) 43 if (ticker == null) { 44 return; 45 } 46 run = false; 47 try { 48 ticker.join(); 49 }catch (InterruptedException x) { 50 Thread.interrupted(); 51 } 52 53 ticker = null; 54 } 55 shutdown()56 public void shutdown() { 57 // Notify each listener of the shutdown. This enables them to 58 // trigger any necessary clean-up. 59 for (TickListener l : listeners) { 60 l.shutdown(); 61 } 62 // Wait for the thread to stop. This prevents warnings in the logs 63 // that the thread is still active when the context stops. 64 stop(); 65 } 66 addTickListener(TickListener listener)67 public void addTickListener(TickListener listener) { 68 if (listeners.add(listener)) { 69 if (counter.incrementAndGet()==1) { 70 start(); 71 } 72 } 73 74 } 75 removeTickListener(TickListener listener)76 public void removeTickListener(TickListener listener) { 77 if (listeners.remove(listener)) { 78 if (counter.decrementAndGet()==0) { 79 stop(); 80 } 81 } 82 } 83 84 @Override run()85 public void run() { 86 try { 87 88 Stock[] stocks = new Stock[] { new Stock("GOOG", 435.43), 89 new Stock("YHOO", 27.88), new Stock("ASF", 1015.55), }; 90 Random r = new Random(System.currentTimeMillis()); 91 while (run) { 92 for (int j = 0; j < 1; j++) { 93 int i = r.nextInt() % 3; 94 if (i < 0) { 95 i = i * (-1); 96 } 97 Stock stock = stocks[i]; 98 double change = r.nextDouble(); 99 boolean plus = r.nextBoolean(); 100 if (plus) { 101 stock.setValue(stock.getValue() + change); 102 } else { 103 stock.setValue(stock.getValue() - change); 104 } 105 stock.setCnt(++ticknr); 106 for (TickListener l : listeners) { 107 l.tick(stock); 108 } 109 110 } 111 Thread.sleep(850); 112 } 113 } catch (InterruptedException ix) { 114 // Ignore 115 } catch (Exception x) { 116 x.printStackTrace(); 117 } 118 } 119 120 121 public static interface TickListener { tick(Stock stock)122 public void tick(Stock stock); shutdown()123 public void shutdown(); 124 } 125 126 public static final class Stock implements Cloneable { 127 protected static final DecimalFormat df = new DecimalFormat("0.00"); 128 protected final String symbol; 129 protected double value = 0.0d; 130 protected double lastchange = 0.0d; 131 protected int cnt = 0; 132 Stock(String symbol, double initvalue)133 public Stock(String symbol, double initvalue) { 134 this.symbol = symbol; 135 this.value = initvalue; 136 } 137 setCnt(int c)138 public void setCnt(int c) { 139 this.cnt = c; 140 } 141 getCnt()142 public int getCnt() { 143 return cnt; 144 } 145 getSymbol()146 public String getSymbol() { 147 return symbol; 148 } 149 getValue()150 public double getValue() { 151 return value; 152 } 153 setValue(double value)154 public void setValue(double value) { 155 double old = this.value; 156 this.value = value; 157 this.lastchange = value - old; 158 } 159 getValueAsString()160 public String getValueAsString() { 161 return df.format(value); 162 } 163 getLastChange()164 public double getLastChange() { 165 return this.lastchange; 166 } 167 setLastChange(double lastchange)168 public void setLastChange(double lastchange) { 169 this.lastchange = lastchange; 170 } 171 getLastChangeAsString()172 public String getLastChangeAsString() { 173 return df.format(lastchange); 174 } 175 176 @Override hashCode()177 public int hashCode() { 178 return symbol.hashCode(); 179 } 180 181 @Override equals(Object other)182 public boolean equals(Object other) { 183 if (other instanceof Stock) { 184 return this.symbol.equals(((Stock) other).symbol); 185 } 186 187 return false; 188 } 189 190 @Override toString()191 public String toString() { 192 StringBuilder buf = new StringBuilder("STOCK#"); 193 buf.append(getSymbol()); 194 buf.append('#'); 195 buf.append(getValueAsString()); 196 buf.append('#'); 197 buf.append(getLastChangeAsString()); 198 buf.append('#'); 199 buf.append(String.valueOf(getCnt())); 200 return buf.toString(); 201 202 } 203 204 @Override clone()205 public Object clone() { 206 Stock s = new Stock(this.getSymbol(), this.getValue()); 207 s.setLastChange(this.getLastChange()); 208 s.setCnt(this.cnt); 209 return s; 210 } 211 } 212 } 213