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