This is a bit tricky to explain. I'm trying to model a cyclic iterator that would return the next available DataSource
(SQL). So infinite calls would just pick the next source nicely. The new twist comes with the idea that one DataSource might be 'busy' or somewhat. That is abstracted as a 'waiting' datasource. This designation must be set by the user. Here is my idea to implement a cyclic db source in addition to skipping specified sources for a certain amount of time.
Members:
private Map<String,DataSource> dsMap
private final PriorityQueue<WaitingDataSource> waitingDBList;
private final Map<Integer, Object> waitingIndexes;
private int index = 0;
private Integer betweenInterval;
private Boolean usingWaitingDB;
The next method:
@Override
public DataSource next() {
index = index % dsMap.size();
int nextIndex = hasNextDB();
while(nextIndex != -1 && waitingIndexes.containsKey(nextIndex)) nextIndex = hasNextDB();
if(nextIndex != -1) {
index++;
return dsMap.get(context.get(nextIndex));
}
else {
throw new NoSuchElementException("No databases found.");
}
}
The alternative hasNext method:
public int hasNextDB() {
if(usingWaitingDB) {
//if all databases are waiting, see if one is ready to go, or sleep
if(waitingDBList.size() == dsMap.size()) {
WaitingDataSource longest = waitingDBList.peek();
if( (System.currentTimeMillis() - longest.getTimestamp()) < (betweenInterval/1000)) {
return -1;
}
else {
waitingDBList.remove(longest);
return longest.index;
}
}
else {
return index;
}
}
else {
return index;
}
}
Purpose of not using the interface hasNext method: In my original idea, hasNext would simply return false if there are no more datasources available. However, then I would actually have to iterate to find the next datasource if the next one is not available. Additionally, I'm using a priority queue to check if the longest waiting DB has not yet waiting long enough, so we immediately return. Since I already have that, I'm simply returning the oldest's index in the scenario that all databases are full, since we already know which one we want to return.
This is to designate a db as unavailable:
public void addWaitingDB(DataSource ds) {
waitingDBList.add(new WaitingDataSource(ds, System.currentTimeMillis(), index));
waitingIndexes.put(index, placeholder);
}
Here I'm simply keeping track of the indexes that were taken. I initially thought of using an array and just fill the index position with some value, but I didn't want to enforce a limit with the array size.
And the inner wrapper class to the DataSource
:
private class WaitingDataSource {
private final DataSource ds;
private final long timestamp;
private final int index;
WaitingDataSource(DataSource ds, long timestamp, int index) {
this.ds = ds;
this.timestamp = timestamp;
this.index = index;
}
public DataSource getDataSource() {
return ds;
}
public long getTimestamp() {
return timestamp;
}
public int getIndex() {
return index;
}
}
The priority list comparator:
waitingDBList = new PriorityQueue<WaitingDataSource>(CONTEXT_JDBC.size(), new Comparator<WaitingDataSource>() {
@Override
public int compare(WaitingDataSource o1, WaitingDataSource o2) {
if(o1.getTimestamp() < o2.getTimestamp()) {
return 1;
}
else if(o1.getTimestamp() == o2.getTimestamp()) {
return 0;
}
else {
return -1;
}
}
});
I suppose the biggest questions I have here is - am I missing some design pattern that would make this more intuitive. Including the iterator interface seems to only make things more confusing. Additionally, I'm not confident that keeping a Map of unavailable indexes is the best course of action.