package ca.carleton.gcrc.couch.config.listener;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.log4j.Logger;
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.client.ReplicationRequest;
public class ConfigWorkerThread extends Thread {
final static public String REPLICATION_DIRECTION_OUTGOING = "outgoing";
final static public String REPLICATION_DIRECTION_INCOMING = "incoming";
final static public String REPLICATION_DIRECTION_BIDIRECTIONAL = "bidirectional";
final protected Logger logger = Logger.getLogger(this.getClass());
private boolean isShuttingDown = false;
private CouchDesignDocument dd;
private String serverName;
private ConfigListener configListener;
private String lastRevision;
protected ConfigWorkerThread(
CouchDesignDocument dd
,String serverName
,ConfigListener configListener
) {
this.dd = dd;
this.serverName = serverName;
this.configListener = configListener;
}
public void shutdown() {
logger.info("Shutting down config listener worker thread");
synchronized(this) {
isShuttingDown = true;
this.notifyAll();
}
}
@Override
public void run() {
logger.info("Start config listener worker thread");
boolean done = false;
do {
synchronized(this) {
done = isShuttingDown;
}
if( false == done ) {
activity();
}
} while( false == done );
logger.info("Config listener worker thread exiting");
}
private void activity() {
String viewName = "config-by-server-name";
try {
CouchQuery query = new CouchQuery();
query.setViewName(viewName);
query.setStartKey(serverName);
query.setEndKey(serverName);
CouchQueryResults results;
try {
results = dd.performQuery(query);
} catch (Exception e) {
throw new Exception("Error accessing view: "+viewName,e);
}
// Analyze configuration
if( results.getRows().size() < 1 ) {
throw new Exception("Configuration with id "+serverName+" not found.");
}
try {
JSONObject row = results.getRows().get(0);
JSONObject doc = row.getJSONObject("value");
String rev = doc.getString("_rev");
if( null == rev ) {
throw new Exception("Unable to read attribute '_rev'");
} else if( null == lastRevision
|| false == lastRevision.equals(rev)) {
logger.info("Reporting new configuration for "+serverName);
reportNewConfiguration(doc);
lastRevision = rev;
} else {
// Nothing to do
// Wait 60 secs until next cycle
waitMillis(60 * 1000);
}
} catch(Exception e) {
throw new Exception("Error parsing configuration object: "+serverName,e);
}
} catch(Exception e) {
logger.error("Error while handling configuration object: "+serverName,e);
waitMillis(60 * 1000); // wait a minute on errors
logger.info("Restart configuration work: "+serverName,e);
}
}
private void reportNewConfiguration(JSONObject doc) throws Exception {
CouchConfig config = new CouchConfig();
config.setRevision( doc.getString("_rev") );
config.setServer( doc.getString("server") );
if( doc.containsKey("replicationInterval") ) {
int interval = doc.getInt("replicationInterval");
config.setReplicationInterval( new Integer(interval) );
}
JSONArray replications = doc.optJSONArray("replications");
if( null != replications ) {
for(int i=0, e=replications.size(); i<e; ++i) {
JSONObject replicationJson = replications.getJSONObject(i);
String direction = replicationJson.optString("direction");
// Outgoing
if( REPLICATION_DIRECTION_OUTGOING.equals(direction)
|| REPLICATION_DIRECTION_BIDIRECTIONAL.equals(direction) ) {
ReplicationRequest replicationRequest = new ReplicationRequest();
replicationRequest.setSourceDbName( replicationJson.getString("localDbName") );
replicationRequest.setTargetDbName( replicationJson.getString("remoteDbName") );
replicationRequest.setTargetServerUrl( replicationJson.getString("remoteServerUrl") );
// String remoteUserName = replicationJson.optString("remoteUserName", null);
// if( null != remoteUserName ) {
// replicationRequest.setTargetUserName( remoteUserName );
// }
//
// String remotePassword = replicationJson.optString("remotePassword", null);
// if( null != remotePassword ) {
// replicationRequest.setTargetPassword( remotePassword );
// }
replicationRequest.setTargetUserName( ReplicationRequest.REMOTE_USER_NAME );
replicationRequest.setTargetPassword( ReplicationRequest.REMOTE_USER_PASSWORD );
String filterName = replicationJson.optString("filterName", null);
if( null != filterName ) {
replicationRequest.setFilter( filterName );
}
boolean continuous = replicationJson.optBoolean("continuous",false);
if( continuous ) {
replicationRequest.setContinuous(true);
}
config.addReplication(replicationRequest);
}
// Incoming
if( REPLICATION_DIRECTION_INCOMING.equals(direction)
|| REPLICATION_DIRECTION_BIDIRECTIONAL.equals(direction) ) {
ReplicationRequest replicationRequest = new ReplicationRequest();
replicationRequest.setTargetDbName( replicationJson.getString("localDbName") );
replicationRequest.setSourceDbName( replicationJson.getString("remoteDbName") );
replicationRequest.setSourceServerUrl( replicationJson.getString("remoteServerUrl") );
// String remoteUserName = replicationJson.optString("remoteUserName", null);
// if( null != remoteUserName ) {
// replicationRequest.setSourceUserName( remoteUserName );
// }
//
// String remotePassword = replicationJson.optString("remotePassword", null);
// if( null != remotePassword ) {
// replicationRequest.setSourcePassword( remotePassword );
// }
replicationRequest.setSourceUserName( ReplicationRequest.REMOTE_USER_NAME );
replicationRequest.setSourcePassword( ReplicationRequest.REMOTE_USER_PASSWORD );
String filterName = replicationJson.optString("filterName", null);
if( null != filterName ) {
replicationRequest.setFilter( filterName );
}
boolean continuous = replicationJson.optBoolean("continuous",false);
if( continuous ) {
replicationRequest.setContinuous(true);
}
config.addReplication(replicationRequest);
}
}
};
if( null != configListener ) {
configListener.configurationUpdated(config);
}
}
private boolean waitMillis(int millis) {
synchronized(this) {
if( true == isShuttingDown ) {
return false;
}
try {
this.wait(millis);
} catch (InterruptedException e) {
// Interrupted
return false;
}
}
return true;
}
}