![JAR search and dependency download from the Maven repository](/logo.png)
ca.carleton.gcrc.couch.date.impl.DateRobotThread Maven / Gradle / Ivy
package ca.carleton.gcrc.couch.date.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ca.carleton.gcrc.couch.client.CouchDb;
import ca.carleton.gcrc.couch.client.CouchDbChangeListener;
import ca.carleton.gcrc.couch.client.CouchDbChangeMonitor;
import ca.carleton.gcrc.couch.client.CouchDesignDocument;
import ca.carleton.gcrc.couch.client.CouchQuery;
import ca.carleton.gcrc.couch.client.CouchQueryResults;
import ca.carleton.gcrc.couch.date.cluster.CouchTreeOperations;
import ca.carleton.gcrc.couch.date.cluster.Tree;
import ca.carleton.gcrc.couch.date.cluster.TreeElement;
import ca.carleton.gcrc.couch.date.cluster.TreeInsertProcess;
import ca.carleton.gcrc.couch.date.cluster.TreeNode;
import ca.carleton.gcrc.couch.date.cluster.TreeOperations;
import ca.carleton.gcrc.couch.utils.CouchNunaliitUtils;
public class DateRobotThread extends Thread implements CouchDbChangeListener {
static final public int DELAY_NO_WORK_POLLING = 5 * 1000; // 5 seconds
static final public int DELAY_NO_WORK_MONITOR = 60 * 1000; // 1 minute
static final public int DELAY_ERROR = 60 * 1000; // 1 minute
static public class Work {
public enum Type {
PROCESS_DOC
,RELOAD_TREE
,TRIM_LEGACY_NODES
};
public Type type;
public String docId;
}
final protected Logger logger = LoggerFactory.getLogger(this.getClass());
private boolean isShuttingDown = false;
private CouchDesignDocument atlasDesign;
private Set docIdsToSkip = new HashSet();
private Tree clusterTree;
private boolean reloadTree = false;
private int noWorkDelayInMs = DELAY_NO_WORK_POLLING;
public DateRobotThread(CouchDesignDocument atlasDesign, Tree clusterTree) throws Exception {
this.atlasDesign = atlasDesign;
this.clusterTree = clusterTree;
noWorkDelayInMs = DELAY_NO_WORK_POLLING;
CouchDbChangeMonitor changeMonitor = atlasDesign.getDatabase().getChangeMonitor();
if( null != changeMonitor ){
changeMonitor.addChangeListener(this);
noWorkDelayInMs = DELAY_NO_WORK_MONITOR;
}
}
public void shutdown() {
logger.info("Shutting down date worker thread");
synchronized(this) {
isShuttingDown = true;
this.notifyAll();
}
}
@Override
public void run() {
logger.info("Start date worker thread");
boolean done = false;
do {
synchronized(this) {
done = isShuttingDown;
}
if( false == done ) {
activity();
}
} while( false == done );
logger.info("Submission date thread exiting");
}
private void activity() {
// Check for work
Collection allWork = null;
try {
allWork = getWork();
} catch (Exception e) {
logger.error("Error obtaining work",e);
waitMillis(DELAY_ERROR); // wait a minute
return;
}
if( null == allWork || allWork.size() < 1 ) {
// Nothing to do, wait
waitMillis(noWorkDelayInMs);
return;
} else {
for(Work work : allWork){
try {
// Handle this work
performWork(work);
} catch(Exception e) {
logger.error("Error performing work "+work.type,e);
synchronized(this) {
reloadTree = true;
}
return;
}
}
}
}
private Collection getWork() throws Exception {
List allWork = new Vector();
boolean mustReloadTree = false;
synchronized(this){
mustReloadTree = reloadTree;
}
// Check if cluster date tree document was deleted
// if( false == reloadTree ){
// boolean exists = clusterTree.getOperations().treeExists();
// if( false == exists ){
// reloadTree = true;
// }
// }
if( mustReloadTree ) {
Work work = new Work();
work.type = Work.Type.RELOAD_TREE;
allWork.add(work);
} else {
Set docIds = new HashSet();
// Check for work: all un-indexed date intervals
{
CouchQuery query = new CouchQuery();
query.setViewName("date-index");
query.setReduce(false);
JSONArray keys = new JSONArray();
keys.put(JSONObject.NULL);
query.setKeys(keys);
CouchQueryResults results = atlasDesign.performQuery(query);
synchronized(this) { // protect docIdsToSkip
for(JSONObject row : results.getRows()) {
String id = row.optString("id");
if( null != id
&& false == docIdsToSkip.contains(id) ) {
// Found some work
docIds.add(id);
}
}
}
}
// Check for work: all legacy nodes
{
List legacyNodes = clusterTree.getLegacyNodes();
if( legacyNodes.size() > 0 ){
JSONArray keys = new JSONArray();
for(TreeNode legacyNode : legacyNodes){
keys.put(legacyNode.getClusterId());
}
CouchQuery query = new CouchQuery();
query.setViewName("date-index");
query.setReduce(false);
query.setReduce(false);
query.setKeys(keys);
CouchQueryResults results = atlasDesign.performQuery(query);
synchronized(this) { // protect docIdsToSkip
for(JSONObject row : results.getRows()) {
String id = row.optString("id");
if( null != id
&& false == docIdsToSkip.contains(id) ) {
// Found some work
docIds.add(id);
}
}
}
}
}
for(String docId : docIds){
Work work = new Work();
work.type = Work.Type.PROCESS_DOC;
work.docId = docId;
allWork.add(work);
}
}
if( allWork.size() < 1 ){
// Check if legacy nodes need to be trimmed
if( clusterTree.getLegacyNodes().size() > 0 ){
Work work = new Work();
work.type = Work.Type.TRIM_LEGACY_NODES;
allWork.add(work);
}
}
return allWork;
}
public void performWork(Work work) throws Exception {
if( Work.Type.PROCESS_DOC == work.type ){
try {
performProcessDocument(work.docId);
} catch(Exception e) {
synchronized(this) { // protect docIdsToSkip
docIdsToSkip.add(work.docId);
}
throw e;
}
} else if( Work.Type.RELOAD_TREE == work.type ) {
performReloadTree();
} else if( Work.Type.TRIM_LEGACY_NODES == work.type ) {
performLegacyNodeTrimming();
} else {
throw new Exception("Unrecognized work: "+work.type);
}
}
public void performProcessDocument(String docId) throws Exception {
// Get submission document
CouchDb db = atlasDesign.getDatabase();
JSONObject jsonDoc = db.getDocument(docId);
List dateStructures = CouchNunaliitUtils.findStructuresOfType("date", jsonDoc);
List treeElements = new ArrayList(dateStructures.size());
for(JSONObject s : dateStructures){
DateStructureElement e = new DateStructureElement(s);
treeElements.add(e);
}
NowReference now = NowReference.now();
TreeInsertProcess.Result treeInsertInfo = TreeInsertProcess.insertElements(clusterTree, treeElements, now);
if( treeInsertInfo.isTreeModified() ){
TreeOperations ops = clusterTree.getOperations();
ops.saveTree(clusterTree);
logger.info("Modified cluster tree");
}
// Update content of document
boolean documentUpdated = false;
Map> insertions = treeInsertInfo.getInsertions();
for(Integer clusterId : insertions.keySet()){
for(TreeElement treeElement : insertions.get(clusterId)){
if( treeElement instanceof DateStructureElement ){
DateStructureElement e = (DateStructureElement)treeElement;
if( clusterId != e.getClusterId() ){
e.setClusterId(clusterId);
documentUpdated = true;
}
}
}
}
if( documentUpdated ) {
db.updateDocument(jsonDoc);
logger.info("Indexed date structures: "+jsonDoc.getString("_id"));
}
}
private void performReloadTree() throws Exception {
try {
clusterTree.reload();
reloadTree = false;
} catch (Exception e) {
throw new Exception("Error reloading cluster tree",e);
}
}
private void performLegacyNodeTrimming() throws Exception {
TreeOperations ops = clusterTree.getOperations();
clusterTree.clearLegacyNodes();
ops.saveTree(clusterTree);
logger.info("Cleared legacy nodes");
}
private boolean waitMillis(int millis) {
synchronized(this) {
if( true == isShuttingDown ) {
return false;
}
try {
this.wait(millis);
} catch (InterruptedException e) {
// Interrupted
return false;
}
}
return true;
}
@Override
public void change(
CouchDbChangeListener.Type type
,String docId
,String rev
,JSONObject rawChange
,JSONObject doc
) {
synchronized(this){
docIdsToSkip.remove(docId);
if( CouchTreeOperations.DATE_CLUSTER_DOC_ID.equals(docId) ){
reloadTree = true;
}
this.notifyAll();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy