package nl.topicus.onderwijs.dashboard.modules;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Resource;
import nl.topicus.onderwijs.dashboard.datasources.Alerts;
import nl.topicus.onderwijs.dashboard.datasources.AverageRequestTime;
import nl.topicus.onderwijs.dashboard.datasources.DataSourceAnnotationReader;
import nl.topicus.onderwijs.dashboard.datasources.NumberOfUsers;
import nl.topicus.onderwijs.dashboard.datatypes.Alert;
import nl.topicus.onderwijs.dashboard.datatypes.BuienRadar;
import nl.topicus.onderwijs.dashboard.datatypes.Commit;
import nl.topicus.onderwijs.dashboard.datatypes.Dot;
import nl.topicus.onderwijs.dashboard.datatypes.DotColor;
import nl.topicus.onderwijs.dashboard.datatypes.Event;
import nl.topicus.onderwijs.dashboard.datatypes.Issue;
import nl.topicus.onderwijs.dashboard.datatypes.IssuePriority;
import nl.topicus.onderwijs.dashboard.datatypes.IssueSeverity;
import nl.topicus.onderwijs.dashboard.datatypes.IssueStatus;
import nl.topicus.onderwijs.dashboard.datatypes.TwitterStatus;
import nl.topicus.onderwijs.dashboard.datatypes.WeatherReport;
import nl.topicus.onderwijs.dashboard.datatypes.WeatherType;
import nl.topicus.onderwijs.dashboard.datatypes.train.Train;
import nl.topicus.onderwijs.dashboard.datatypes.train.TrainType;
import nl.topicus.onderwijs.dashboard.keys.Key;
import nl.topicus.onderwijs.dashboard.keys.Project;
import nl.topicus.onderwijs.dashboard.keys.Summary;
import nl.topicus.onderwijs.dashboard.modules.wettercom.WetterComService;
import org.apache.wicket.util.time.Duration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository("random")
public class RandomDataRepositoryImpl extends TimerTask implements
DashboardRepository {
private static final long HEX_10_DIGITS = 256L * 256L * 256L * 256L * 256L;
private static final Object NULL = new Object();
@Autowired
@Resource(name = "online")
private DashboardRepository base;
private Set<Class<? extends DataSource<?>>> sources = new HashSet<Class<? extends DataSource<?>>>();
private ConcurrentHashMap<String, Object> dataCache = new ConcurrentHashMap<String, Object>();
private Map<Key, List<Event>> eventCache = new HashMap<Key, List<Event>>();
private Timer timer;
public RandomDataRepositoryImpl() {
start();
}
public void stop() {
if (timer != null) {
timer.cancel();
timer = null;
}
}
public void start() {
if (timer == null) {
timer = new Timer("Random Data Updater", true);
timer.scheduleAtFixedRate(this, 0, Duration.seconds(5)
.getMilliseconds());
}
}
public <T extends DataSource<?>> void addDataSource(Key key,
Class<T> datasourceType, T dataSource) {
sources.add(datasourceType);
base.addDataSource(key, datasourceType, dataSource);
}
public List<Project> getProjects() {
return base.getProjects();
}
public <T extends Key> List<T> getKeys(Class<T> keyType) {
return base.getKeys(keyType);
}
public Collection<DataSource<?>> getData(Key key) {
Collection<DataSource<?>> ret = new ArrayList<DataSource<?>>();
for (Class<? extends DataSource<?>> curDataSource : sources)
ret.add(createDataSource(key, curDataSource));
return ret;
}
@Override
public <T extends DataSource<?>> Map<Key, T> getData(Class<T> datasource) {
Map<Key, T> ret = new HashMap<Key, T>();
for (Key curKey : getKeys(Key.class)) {
T dataSource = createDataSource(curKey, datasource);
if (dataSource != null)
ret.put(curKey, dataSource);
}
return ret;
}
@SuppressWarnings("unchecked")
private <T extends DataSource<?>> T createDataSource(final Key key,
final Class<T> dataSource) {
// summary is a special case, use the original datasource
if (key.equals(Summary.get()))
return base.getData(dataSource).get(key);
// summary is a special case, use the original datasource
if (Alerts.class.isAssignableFrom(dataSource)
&& !(key instanceof Project))
return null;
final DataSourceSettings settings = DataSourceAnnotationReader
.getSettings(dataSource);
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Random random = new Random();
if (method.getName().equals("getValue")) {
String dataKey = key.getCode() + "-" + dataSource.getName();
Object value;
if (settings.type().equals(Integer.class)) {
if (settings.list()) {
List<Integer> ret = new ArrayList<Integer>();
for (int count = 0; count < 5; count++)
ret.add(random.nextInt(200));
value = ret;
} else {
value = createInteger(key, dataSource, random);
}
} else if (settings.type().equals(Duration.class))
value = Duration
.milliseconds(Math.round(Math.random() * 100000000));
else if (settings.type().equals(String.class))
value = "random";
else if (settings.type().equals(Date.class)) {
value = new Date();
} else if (settings.type().equals(WeatherReport.class)) {
value = createRandomWeather();
} else if (settings.type().equals(Dot.class)
&& settings.list()) {
List<Dot> ret = new ArrayList<Dot>();
for (int count = 0; count < 5; count++) {
ret.add(new Dot(
DotColor.values()[random.nextInt(4)],
Integer.toString(count + 1)));
}
value = ret;
} else if (settings.type().equals(Train.class)
&& settings.list()) {
value = createRandomTrains();
} else if (settings.type().equals(Alert.class)
&& settings.list()) {
value = createRandomAlerts(key);
} else if (settings.type().equals(Commit.class)
&& settings.list()) {
value = createRandomCommits(key);
} else if (settings.type().equals(Issue.class)
&& settings.list()) {
value = createRandomIssues(key);
} else if (settings.type().equals(Event.class)
&& settings.list()) {
value = createRandomEvents(key);
} else if (settings.type().equals(TwitterStatus.class)
&& settings.list()) {
value = createRandomTweets(key);
} else if (settings.type().equals(BuienRadar.class)) {
value = createRandomBuien();
} else
throw new IllegalStateException("Unsupported type "
+ settings.type());
Object ret = dataCache.putIfAbsent(dataKey, value);
ret = ret == null ? value : ret;
return ret == NULL ? null : ret;
} else if (method.getName().equals("toString")) {
String dataKey = key.getCode() + "-" + dataSource.getName();
return dataKey;
}
throw new UnsupportedOperationException();
}
private Object createInteger(Key key,
Class<? extends DataSource<?>> dataSource, Random random) {
if (dataSource.equals(NumberOfUsers.class)) {
if (key.getCode().equals("iris")
|| key.getCode().equals("atvo"))
return NULL;
} else if (dataSource.equals(AverageRequestTime.class)) {
if (key.getCode().equals("iris")
|| key.getCode().equals("atvo_ouders")
|| key.getCode().equals("parnassys_ouders"))
return NULL;
}
return random.nextInt(1000);
}
private WeatherReport createRandomWeather() {
Random random = new Random();
WeatherReport ret = new WeatherReport();
ret.setMaxTemperature(Math.round(random.nextDouble() * 500.0 - 150.0) / 10.0);
ret.setMinTemperature(Math.round(ret.getMaxTemperature() * 10.0
- random.nextDouble() * 100.0) / 10.0);
ret.setRainfallProbability(random.nextInt(100));
ret.setType(WeatherType.values()[random.nextInt(WeatherType
.values().length)]);
ret.setWindDirection(random.nextInt(360));
ret.setWindSpeed(Math.round(random.nextDouble() * 1000.0) / 10.0);
double lat = 52.25;
double lon = 6.2;
ret.setSunrise(WetterComService.getSunrize(lat, lon));
ret.setSunset(WetterComService.getSunset(lat, lon));
return ret;
}
private BuienRadar createRandomBuien() {
Random random = new Random();
BuienRadar ret = new BuienRadar();
Integer[] rainForecast = new Integer[12];
for (int i = 0; i < rainForecast.length; i++) {
rainForecast[i] = 500 * random.nextInt(2);
}
ret.setRainForecast(rainForecast);
return ret;
}
private List<Train> createRandomTrains() {
Random random = new Random();
List<Train> ret = new ArrayList<Train>();
for (int count = 0; count < 10; count++) {
Train train = new Train();
train.setType(TrainType.values()[random.nextInt(TrainType
.values().length)]);
train.setDestination("random random random centraal");
int minute = random.nextInt(60);
train.setDepartureTime(random.nextInt(24) + ":"
+ (minute < 10 ? "0" : "") + minute);
train.setDelay(random.nextInt(10));
train.setPlatform(Integer.toString(random.nextInt(10)));
ret.add(train);
}
Collections.sort(ret, new Comparator<Train>() {
@Override
public int compare(Train o1, Train o2) {
return o1.getKey().compareTo(o2.getKey());
}
});
return ret;
}
private List<Alert> createRandomAlerts(Key key) {
if (!(key instanceof Project))
return null;
Random random = new Random();
List<Alert> ret = new ArrayList<Alert>();
for (int count = 0; count < random.nextInt(3); count++) {
Alert alert = new Alert();
alert.setProject(key);
alert.setOverlayVisible(false);
alert.setColor(DotColor.values()[random.nextInt(3)]);
int minute = random.nextInt(60);
alert.setTime(random.nextInt(24) + ":"
+ (minute < 10 ? "0" : "") + minute);
alert.setMessage("random exception with long message");
ret.add(alert);
}
Collections.sort(ret, new Comparator<Alert>() {
@Override
public int compare(Alert o1, Alert o2) {
return o1.getKey().compareTo(o2.getKey());
}
});
return ret;
}
private List<Commit> createRandomCommits(Key key) {
Random random = new Random();
List<Commit> ret = new ArrayList<Commit>();
for (int count = 0; count < 5; count++) {
Commit commit = new Commit();
commit.setProject(key);
long rev = Math.abs(random.nextLong());
if (rev < HEX_10_DIGITS)
rev += HEX_10_DIGITS + 1;
commit.setRevision(Long.toString(rev, 16).substring(0, 8));
commit.setDateTime(new Date(System.currentTimeMillis()
- random.nextInt(3600000)));
commit.setMessage("random commit with long message");
commit.setAuthor("random");
ret.add(commit);
}
return ret;
}
private List<Issue> createRandomIssues(Key key) {
Random random = new Random();
List<Issue> ret = new ArrayList<Issue>();
for (int count = 0; count < 5; count++) {
Issue issue = new Issue();
issue.setProject(key);
issue.setId(random.nextInt(100000));
issue.setDateTime(new Date(System.currentTimeMillis()
- random.nextInt(3600000)));
issue.setSummary("random issue with long message");
issue.setStatus(IssueStatus.NEW);
issue.setSeverity(IssueSeverity.values()[random
.nextInt(IssueSeverity.values().length)]);
issue.setPriority(IssuePriority.values()[random
.nextInt(IssuePriority.values().length)]);
ret.add(issue);
}
return ret;
}
private synchronized List<Event> createRandomEvents(Key key) {
List<Event> ret = eventCache.get(key);
if (ret != null)
return ret;
Random random = new Random();
ret = new ArrayList<Event>();
for (int count = 0; count < 2; count++) {
Event event = new Event();
event.setKey(key);
event.setTitle("random");
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_YEAR, random.nextInt(30));
event.setDateTime(cal.getTime());
event.setMajor(random.nextInt(5) == 0);
if (event.isMajor())
event.getTags().add("#major");
event.setColor(Integer.toHexString(random
.nextInt(256 * 256 * 256)));
while (event.getColor().length() < 0)
event.setColor("0" + event.getColor());
event.setColor("#" + event.getColor());
ret.add(event);
}
Collections.sort(ret, new Comparator<Event>() {
@Override
public int compare(Event o1, Event o2) {
return o1.getDateTime().compareTo(o2.getDateTime());
}
});
eventCache.put(key, ret);
return ret;
}
private List<TwitterStatus> createRandomTweets(Key key) {
Random random = new Random();
List<TwitterStatus> ret = new ArrayList<TwitterStatus>();
for (int count = 0; count < 10; count++) {
TwitterStatus status = new TwitterStatus(key);
status.setDate(new Date(System.currentTimeMillis()
- random.nextInt(24 * 3600 * 1000)));
status.setTags(Collections.<String> emptyList());
status.setUser("random");
status.setText("random tweet at " + count);
ret.add(status);
}
return ret;
}
};
return (T) Proxy.newProxyInstance(getClass().getClassLoader(),
new Class[] { dataSource }, handler);
}
@Override
public void run() {
dataCache.clear();
}
}