package ca.carleton.gcrc.couch.app;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.security.MessageDigest;
import net.sf.json.JSONObject;
import net.sf.json.util.JSONTokener;
import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
import ca.carleton.gcrc.couch.client.CouchDb;
public class DesignDocumentPush {
final protected Logger logger = Logger.getLogger(this.getClass());
final static public String DESIGN_DOC_SIG_KEY = "nunaliit_signature";
private CouchDb couchDb;
private String designDocName;
private JSONLoader jsonLoader;
public DesignDocumentPush() {
}
public DesignDocumentPush(
CouchDb couchDb
,String designDocName
,File ddDir
) throws Exception {
this.couchDb = couchDb;
this.designDocName = designDocName;
this.jsonLoader = new JSONFileLoader(ddDir);
}
public DesignDocumentPush(
CouchDb couchDb
,String designDocName
,JSONLoader jsonLoader
) {
this.couchDb = couchDb;
this.designDocName = designDocName;
this.jsonLoader = jsonLoader;
}
public CouchDb getCouchDb() {
return couchDb;
}
public void setCouchDb(CouchDb couchDb) {
this.couchDb = couchDb;
}
public String getDesignDocName() {
return designDocName;
}
public void setDesignDocName(String designDocName) {
this.designDocName = designDocName;
}
public JSONLoader getJsonLoader() {
return jsonLoader;
}
public void setJsonLoader(JSONLoader jsonLoader) {
this.jsonLoader = jsonLoader;
}
public void push() throws Exception {
// Load design document
StringWriter ddWriter = new StringWriter();
JSONObject designDoc = new JSONObject();
try {
jsonLoader.write(ddWriter);
JSONTokener jsonTokener = new JSONTokener(ddWriter.toString());
Object obj = jsonTokener.nextValue();
if( obj instanceof JSONObject ) {
designDoc = (JSONObject)obj;
} else {
throw new Exception("Unexpected returned object type: "+obj.getClass().getSimpleName());
}
designDoc.put("_id", "_design/"+designDocName);
} catch(Exception e) {
throw new Exception("Unable to load design document", e);
}
// Compute signature
String signature = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(baos, "UTF-8");
osw.write(ddWriter.toString());
osw.flush();
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(baos.toByteArray());
byte[] sigBytes = md.digest();
// B64
byte[] encoded = Base64.encodeBase64(sigBytes);
ByteArrayInputStream bais = new ByteArrayInputStream(encoded);
InputStreamReader isr = new InputStreamReader(bais, "UTF-8");
BufferedReader br = new BufferedReader(isr);
signature = br.readLine();
// Add signature to JSON doc
designDoc.put(DESIGN_DOC_SIG_KEY, signature);
logger.info("Design document signature (_design/"+designDocName+"): "+signature);
} catch(Exception e) {
throw new Exception("Unable to compute signature on design document", e);
}
// Check if design doc exists
boolean creationRequired = false;
boolean updateNeeded = false;
try {
if( false == couchDb.documentExists("_design/"+designDocName) ) {
creationRequired = true;
} else {
// Check if it is the right signature
JSONObject currentDesignDoc = couchDb.getDocument("_design/"+designDocName);
if( false == currentDesignDoc.containsKey(DESIGN_DOC_SIG_KEY) ) {
updateNeeded = true;
} else {
String currentSignature = currentDesignDoc.getString(DESIGN_DOC_SIG_KEY);
if( false == signature.equals(currentSignature) ) {
updateNeeded = true;
}
}
// Update revision
designDoc.put("_rev", currentDesignDoc.get("_rev"));
}
} catch(Exception e) {
throw new Exception("Unable to access current design document", e);
}
// Upload, if required
if( creationRequired ) {
logger.info("Creating design document: _design/"+designDocName);
try {
couchDb.createDocument(designDoc);
} catch (Exception e) {
throw new Exception("Unable to create design document", e);
}
} else if( updateNeeded ) {
logger.info("Uploading design document: _design/"+designDocName);
try {
couchDb.updateDocument(designDoc);
} catch (Exception e) {
throw new Exception("Unable to update design document", e);
}
}
}
}