Permalink
Browse files

CouchResponse

  - Change constructor (CouchResponse(HttpMethod) to use method.getResponseBodyAsString(); instead of a Buffered 

method.getResponseBodyAsStream(), was getting "Bad chunk size: HTTP/1.1 500 Internal Server Error" for large responses
  - Added correct handle of DELETE w/ status code 200 (couchdb API change)
 
Database
  - Added URLEncoder.encode(id, "utf-8) for id references (save, delete, etc)
    - method signatures changed to throws IOException because of URLEncoder()
  - Added bulkSaveDocuments(Document[]), with performs POST /db/_bulk_docs 

Document
  - Added check for "id" id, in addition to "_id", to String getId() (couchdb API change). 
  - Added check for "rev" rev, in addition to "_rev", to String getRev() (couchdb API change). 
  - Added throws IOException  for some methods, because of URLEncode.encode() additions to Database
  - Fixed getView(String) to use "_design" (couchdb API change)
  - Fixed addView(String, String) to use "_design" (couchdb API change)
  - Fixed deleteView(String) to use "_design" (couchdb API change)
  - getJSONObject() wrapped IOException to RuntimeException, otherwise had to change a lot of JSON method signatures to 

throw IOException (didn't think that was necessary)


Session
  - Removed URLEncoder.encode() from buildUrl(String) because it was incorrectly encoding "/"'s from <dbname>/<etc>
  - Added setHttpTimeout(int ms) to enable timeout for HttpClient 
  - Fixed content type check for post(String,String,String) temp_view (now all are json mime)

JUnit Tests
  - Added and fixed some tests.
  - NOTE: addHoc and add new views still do not work.
  • Loading branch information...
1 parent 7332798 commit ff794f774165fcc4060624db0ea8435f29fc7f95 william.kinney committed Sep 17, 2008
View
21 src/java/com/fourspaces/couchdb/CouchResponse.java
@@ -16,9 +16,7 @@
package com.fourspaces.couchdb;
-import java.io.BufferedReader;
import java.io.IOException;
-import java.io.InputStreamReader;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
@@ -64,21 +62,14 @@
*/
CouchResponse(HttpMethod method) throws IOException {
methodName=method.getName();
- String line = "";
- BufferedReader reader = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream()));
- while ((line=reader.readLine())!=null) {
- if (body==null) {
- body = "";
- } else {
- body +="\n";
- }
- body+=line;
- }
+ headers = method.getResponseHeaders();
+ body = method.getResponseBodyAsString();
+
path = method.getPath();
if (method.getQueryString()!=null && !method.getQueryString().equals("")) {
path += "?"+method.getQueryString();
}
- headers = method.getResponseHeaders();
+
statusCode=method.getStatusCode();
if (
@@ -93,8 +84,8 @@
} else if (
(methodName.equals("PUT") && statusCode==201) ||
(methodName.equals("POST") && statusCode==201) ||
- (methodName.equals("DELETE") && statusCode==202)
- ) {
+ (methodName.equals("DELETE") && statusCode==202) ||
+ (methodName.equals("DELETE") && statusCode==200)) {
ok = JSONObject.fromObject(body).getBoolean("ok");
} else if ((method.getName().equals("GET") || method.getName().equals("POST")) && statusCode==200) {
View
92 src/java/com/fourspaces/couchdb/Database.java
@@ -16,9 +16,14 @@
package com.fourspaces.couchdb;
+import java.io.IOException;
+import java.net.URLEncoder;
+
+import net.sf.json.JSONArray;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
+import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -87,6 +92,16 @@ public ViewResults getAllDocuments() {
return view(new View("_all_docs"), false);
}
+ /**
+ * Runs the standard "_all_docs" view on this database, with count
+ * @return ViewResults - the results of the view... this can be iterated over to get each document.
+ */
+ public ViewResults getAllDocumentsWithCount(int count) {
+ View v = new View("_all_docs");
+ v.setCount(count);
+ return view(v, false);
+ }
+
/**
* Runs "_all_docs_by_update_seq?startkey=revision" view on this database
* @return ViewResults - the results of the view... this can be iterated over to get each document.
@@ -160,7 +175,11 @@ public ViewResults adhoc(String function) {
* @return
*/
public ViewResults adhoc(AdHocView view) {
- CouchResponse resp = session.post(name+"/_temp_view", view.getFunction());
+
+ JSONObject ahViewJSONObj = new JSONObject();
+ ahViewJSONObj.accumulate("map", view.getFunction());
+
+ CouchResponse resp = session.post(name+"/_temp_view", ahViewJSONObj.toString());
if (resp.isOk()) {
ViewResults results = new ViewResults(view,resp.getBodyAsJSON());
results.setDatabase(this);
@@ -184,12 +203,12 @@ public ViewResults adhoc(AdHocView view) {
* @param doc
* @param docId
*/
- public void saveDocument(Document doc, String docId) {
+ public void saveDocument(Document doc, String docId) throws IOException {
CouchResponse resp;
if (docId==null || docId.equals("")) {
resp= session.post(name,doc.getJSONObject().toString());
} else {
- resp= session.put(name+"/"+docId,doc.getJSONObject().toString());
+ resp= session.put(name+"/"+ URLEncoder.encode(docId, "utf-8"),doc.getJSONObject().toString());
}
if (resp.isOk()) {
@@ -211,16 +230,53 @@ public void saveDocument(Document doc, String docId) {
* Save a document w/o specifying an id (can be null)
* @param doc
*/
- public void saveDocument(Document doc) {
- saveDocument(doc,doc.getId());
+ public void saveDocument(Document doc) throws IOException {
+ saveDocument(doc, doc.getId());
+ }
+
+ public void bulkSaveDocuments(Document[] documents) throws IOException {
+ CouchResponse resp = null;
+
+ final JSONObject jsonObject = new JSONObject();
+
+ jsonObject.accumulate("docs", documents);
+
+
+ resp = session.post(name + "/_bulk_docs", jsonObject.toString());
+
+
+ if (resp.isOk()) {
+ // TODO set Ids and revs and name (db)
+ final JSONObject respJsonObj = resp.getBodyAsJSON();
+ JSONArray respJsonArray = (JSONArray) respJsonObj.get("new_revs");
+ JSONObject respObj = null;
+ String id = null;
+ String rev = null;
+ for (int i = 0; i < documents.length; i++) {
+ respObj = respJsonArray.getJSONObject(i);
+ id = respObj.getString("id");
+ rev = respObj.getString("rev");
+ if (StringUtils.isBlank(documents[i].getId())) {
+ documents[i].setId(id);
+ documents[i].setRev(rev);
+ } else if (StringUtils.isNotBlank(documents[i].getId()) && documents[i].getId().equals(id)) {
+ documents[i].setRev(rev);
+ } else {
+ log.warn("returned bulk save array in incorrect order, saved documents do not have updated rev or ids");
+ }
+ documents[i].setDatabase(this);
+ }
+ } else {
+ log.warn("Error bulk saving documents - "+resp.getErrorId()+" "+resp.getErrorReason());
+ }
}
/**
* Retrieves a document from the CouchDB database
* @param id
* @return
*/
- public Document getDocument(String id) {
+ public Document getDocument(String id) throws IOException {
return getDocument(id,null,false);
}
/**
@@ -230,7 +286,7 @@ public Document getDocument(String id) {
* @param id
* @return
*/
- public Document getDocumentWithRevisions(String id) {
+ public Document getDocumentWithRevisions(String id) throws IOException {
return getDocument(id,null,true);
}
@@ -240,7 +296,7 @@ public Document getDocumentWithRevisions(String id) {
* @param revision
* @return
*/
- public Document getDocument(String id, String revision) {
+ public Document getDocument(String id, String revision) throws IOException {
return getDocument(id,revision,false);
}
@@ -251,17 +307,17 @@ public Document getDocument(String id, String revision) {
* @param showRevisions
* @return the document
*/
- public Document getDocument(String id, String revision, boolean showRevisions) {
+ public Document getDocument(String id, String revision, boolean showRevisions) throws IOException {
CouchResponse resp;
Document doc = null;
if (revision!=null && showRevisions) {
- resp=session.get(name+"/"+id,"rev="+revision+"&full=true");
+ resp=session.get(name+"/"+URLEncoder.encode(id, "utf-8"),"rev="+revision+"&full=true");
} else if (revision!=null && !showRevisions) {
- resp=session.get(name+"/"+id,"rev="+revision);
+ resp=session.get(name+"/"+URLEncoder.encode(id, "utf-8"),"rev="+revision);
} else if (revision==null && showRevisions) {
- resp=session.get(name+"/"+id,"revs=true");
+ resp=session.get(name+"/"+URLEncoder.encode(id, "utf-8"),"revs=true");
} else {
- resp=session.get(name+"/"+id);
+ resp=session.get(name+"/"+URLEncoder.encode(id, "utf-8"));
}
if (resp.isOk()) {
doc = new Document(resp.getBodyAsJSON());
@@ -276,9 +332,15 @@ public Document getDocument(String id, String revision, boolean showRevisions) {
* Deletes a document
* @param d
* @return was the delete successful?
+ * @throws IllegalArgumentException for blank document id
*/
- public boolean deleteDocument(Document d) {
- CouchResponse resp = session.delete(name+"/"+d.getId() + "?rev=" + d.getRev());
+ public boolean deleteDocument(Document d) throws IOException {
+
+ if (StringUtils.isBlank(d.getId())) {
+ throw new IllegalArgumentException("cannot delete document, doc id is empty");
+ }
+
+ CouchResponse resp = session.delete(name+"/"+URLEncoder.encode(d.getId(), "utf-8").toString() + "?rev=" + d.getRev());
if(resp.isOk()) {
return true;
View
42 src/java/com/fourspaces/couchdb/Document.java
@@ -16,6 +16,7 @@
package com.fourspaces.couchdb;
+import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
@@ -24,6 +25,7 @@
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
+import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -97,7 +99,11 @@ protected void load(JSONObject object2) {
* @return
*/
public String getId() {
- return object.optString("_id");
+ if (StringUtils.isNotBlank(object.optString("_id"))) {
+ return object.optString("_id");
+ } else {
+ return object.optString("id");
+ }
}
public void setId(String id) {
object.put("_id",id);
@@ -107,7 +113,11 @@ public void setId(String id) {
* @return
*/
public String getRev() {
- return object.optString("_rev");
+ if (StringUtils.isNotBlank(object.optString("_rev"))) {
+ return object.optString("_rev");
+ } else {
+ return object.optString("rev");
+ }
}
public void setRev(String rev) {
object.put("_rev", rev);
@@ -118,7 +128,7 @@ public void setRev(String rev) {
* populated with a "full=true" query, then the database will be requeried
* @return
*/
- public String[] getRevisions() {
+ public String[] getRevisions() throws IOException {
String[] revs = null;
if (!object.has("_revs")) {
populateRevisions();
@@ -141,7 +151,7 @@ public void setRev(String rev) {
*/
public View getView(String name) {
View view = null;
- if (object.has("_view_"+name)) {
+ if (object.has("_design/"+name)) {
view = new View(this,name);
}
return view;
@@ -153,13 +163,19 @@ public View getView(String name) {
* <p>
* This isn't persisted until the document is saved.
*
+ * @param design document name
* @param viewName
* @param function
* @return
*/
- public View addView(String viewName, String function) {
- View view = new View(this,viewName, "\""+function+"\"");
- object.put("_view_"+viewName, "\""+function+"\"");
+ public View addView(String designDoc, String viewName, String function) {
+ View view = new View(this, viewName, function);
+ JSONObject o = new JSONObject();
+ JSONObject views = new JSONObject();
+ views.put(viewName, function);
+ o.put("views", views);
+
+ object.put("_design/"+ designDoc, o);
return view;
}
@@ -170,7 +186,7 @@ public View addView(String viewName, String function) {
* @param viewName
*/
public void deleteView(String viewName) {
- object.remove("_view_"+viewName);
+ object.remove("_design/"+viewName);
}
void setDatabase(Database database) {
@@ -181,15 +197,15 @@ void setDatabase(Database database) {
* Loads data from the server for this document. Actually requests a new copy of data from the
* server and uses that to populate this document. This doesn't overwrite any unsaved data.
*/
- public void refresh() {
+ public void refresh() throws IOException {
if (database!=null) {
Document doc = database.getDocument(getId());
log.info("Loading: "+doc.getJSONObject());
load(doc.getJSONObject());
}
}
- protected void populateRevisions() {
+ protected void populateRevisions() throws IOException {
if (database!=null) {
Document doc = database.getDocumentWithRevisions(getId());
log.info("Loading: "+doc.getJSONObject());
@@ -203,7 +219,11 @@ protected void populateRevisions() {
*/
public JSONObject getJSONObject() {
if (!loaded && database!=null && getId()!=null && !getId().equals("")) {
- refresh();
+ try {
+ refresh();
+ } catch (IOException e) {
+ throw new RuntimeException("error in refreshing Document", e);
+ }
}
return object;
}
View
17 src/java/com/fourspaces/couchdb/Session.java
@@ -231,12 +231,6 @@ public boolean deleteDatabase(Database db) {
* @return the absolute URL (hostname/port/etc)
*/
protected String buildUrl(String url) {
- try {
- url = java.net.URLEncoder.encode(url,"utf-8");
- } catch (UnsupportedEncodingException e) {
- log.warn("url encoding error: "+url);
- }
-
return (secure) ? "https://"+host+":"+port+"/"+url : "http://"+host+":"+port+"/"+url;
}
@@ -280,11 +274,7 @@ CouchResponse post(String url, String content, String queryString) {
if (content!=null) {
RequestEntity entity;
try {
- if (url.indexOf("_temp_view") != -1) {
- entity = new StringRequestEntity(content,"text/javascript","UTF-8");
- } else {
- entity = new StringRequestEntity(content,"application/json","UTF-8");
- }
+ entity = new StringRequestEntity(content,"application/json","UTF-8");
post.setRequestEntity(entity);
} catch (UnsupportedEncodingException e) {
log.error(ExceptionUtils.getStackTrace(e));
@@ -390,4 +380,9 @@ protected CouchResponse http(HttpMethod method) {
public CouchResponse getLastResponse() {
return lastResponse;
}
+
+ public void setHttpTimeout(int ms) {
+ this.httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(ms);
+ }
+
}
View
2 src/test/com/fourspaces/couchdb/test/DatabaseTest.java
@@ -47,4 +47,6 @@
assertEquals(sess.getDatabaseNames().size(),old+1);
sess.deleteDatabase("foo2");
}
+
+
}
View
41 src/test/com/fourspaces/couchdb/test/DocumentTest.java
@@ -10,6 +10,8 @@
import net.sf.json.JSONObject;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -18,12 +20,15 @@
import com.fourspaces.couchdb.Document;
import com.fourspaces.couchdb.Session;
import com.fourspaces.couchdb.View;
+import com.fourspaces.couchdb.ViewResults;
public class DocumentTest {
+ Log log = LogFactory.getLog(getClass());
+
Session sess = TestSession.getTestSession();
Database foo;
- @Before public void createTestDB() {
+ @Before public void createTestDB() throws Exception {
foo=sess.createDatabase("foo");
foo.saveDocument(new Document(),"foo");
foo.saveDocument(new Document());
@@ -35,7 +40,7 @@
@Test
- public void update() {
+ public void update() throws Exception {
JSONObject obj = new JSONObject();
obj.put("foo","bar");
obj.accumulate("array", "ar1");
@@ -63,7 +68,7 @@ public void update() {
}
- @Test public void get() {
+ @Test public void get() throws Exception {
JSONObject obj = new JSONObject();
obj.put("foo","bar");
obj.accumulate("array", "ar1");
@@ -86,15 +91,39 @@ public void update() {
@Test
public void list1() {
- View one = new View("_all_docs");
- one.setCount(1);
- assertEquals(foo.view(one).getResults().size(),1 );
+
+ ViewResults vr = foo.getAllDocumentsWithCount(1);
+ assertEquals(vr.getResults().size(),1 );
}
@After
public void deleteAll() {
sess.deleteDatabase("foo");
}
+
+ @Test public void bulkSave() throws Exception {
+
+ Document[] docs = new Document[3];
+ docs[0] = new Document();
+ docs[1] = new Document();
+ docs[2] = new Document();
+
+ docs[0].accumulate("foo", "bar1" + System.currentTimeMillis());
+ docs[1].accumulate("foo", "bar2" + System.currentTimeMillis());
+ docs[2].accumulate("foo", "bar3" + System.currentTimeMillis());
+
+ foo.bulkSaveDocuments(docs);
+
+ for (Document d : docs) {
+ boolean deleted = foo.deleteDocument(d);
+ assertEquals(deleted, true);
+ }
+
+
+
+
+ }
+
}
View
43 src/test/com/fourspaces/couchdb/test/ViewTest.java
@@ -1,27 +1,34 @@
package com.fourspaces.couchdb.test;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.fourspaces.couchdb.Database;
import com.fourspaces.couchdb.Document;
import com.fourspaces.couchdb.Session;
+import com.fourspaces.couchdb.View;
+import com.fourspaces.couchdb.ViewResults;
public class ViewTest {
+
+ Log log = LogFactory.getLog(getClass());
Session sess = TestSession.getTestSession();
Database foo;
- @Before public void createTestDB() {
+ @Before public void createTestDB() throws Exception {
sess.createDatabase("foo");
foo=sess.getDatabase("foo");
Document d = new Document();
d.put("foo","bar");
foo.saveDocument(d);
- System.out.println("known id:"+d.getId());
- System.out.println(foo.getDocument(d.getId()));
+ log.debug("known id:"+d.getId());
+ log.debug(foo.getDocument(d.getId()));
foo.saveDocument(new Document());
foo.saveDocument(new Document());
@@ -36,44 +43,50 @@
@Test public void adhoc() {
int all = foo.getAllDocuments().getResults().size();
- int adhoc = foo.adhoc("function (doc) {map(doc, doc)}").getResults().size();
+ int adhoc = foo.adhoc("function (doc) {emit(null, doc);}").getResults().size();
assertEquals(all,adhoc);
}
@Test public void adhoc2() {
- int adhoc = foo.adhoc("function (doc){ if (doc.foo=='bar'){ map(doc, doc)}}").getResults().size();
+ int adhoc = foo.adhoc("function (doc){ if (doc.foo=='bar'){ emit(doc, doc)}}").getResults().size();
assertEquals(1,adhoc);
}
@Test
- public void addNamed() {
+ public void addNamed() throws Exception {
Document d = new Document();
d.put("foo","bar");
- System.out.println("Saving d");
+ log.debug("Saving d");
foo.saveDocument(d);
Document d2 = new Document();
//d2.put("foo","baz");
- d2.addView("all_documents", "function (doc){ return doc; }");
- d2.addView("testview", "function (doc){ if (doc.foo=='bar'){ return doc; }}");
- System.out.println("Saving d2 - "+d2.getId()+" - "+d2.toString());
+ // d2.addView("all_documents", "function (doc){ return doc; }");
+ d2.addView("viewfoobar", "testview", "function (doc){ if (doc.foo=='bar'){ return doc; }}");
+ log.debug("Saving d2 - "+d2.getId()+" - "+d2.toString());
foo.saveDocument(d2);
- System.out.println("Saved d2 - "+d2.getId()+" - "+d2.toString());
+ log.debug("Saved d2 - "+d2.getId()+" - "+d2.toString());
Document d2_2 = foo.getDocument(d2.getId());
- System.out.println("Saved d2_2 - "+d2_2.toString());
+ log.debug("Saved d2_2 - "+d2_2.toString());
/*
* Doesn't work - CouchDB doens't support named views yet.
*/
- //assertNotNull(d2_2.getView("testview"));
- //assertEquals(2,foo.view(d2.getView("testview")).getResults().size());
+ assertNotNull(d2_2.getView("viewfoobar/testview"));
+ assertEquals(2,foo.view(d2.getView("viewfoobar/testview")).getResults().size());
foo.deleteDocument(d);
foo.deleteDocument(d2);
}
+
+
+
+
+
+
}

0 comments on commit ff794f7

Please sign in to comment.