GithubHelp home page GithubHelp logo

ektorp's Introduction

Ektorp build status Maven Central

Ektorp is a persistence API that uses CouchDB as storage engine. The goal of Ektorp is to combine JPA like functionality with the simplicity and flexibility that CouchDB provides.

Features

Here are some good reasons why you should consider using Ektorp in your project:

  • Rich domain models. With the powerful JSON-object mapping provided by Jackson it is easy to create rich domain models.
  • Schemaless comfort. As CouchDB is schemaless, the database gets out of the way during application development. With a schemaless database, most adjustments to the database become transparent and automatic.
  • Out-of-the-Box CRUD. The generic repository support makes it trivial to create persistence classes.
  • Simple and fluent API.
  • Spring Support. Ektorp features an optional spring support module.
  • Active development. Ektorp is actively developed and has a growing community.
  • Choice of abstraction level. From full object-document mapping to raw streams, Ektorp will never stop you if you need to step down an abstraction level.

Documentation

API-Reference

Simple API

It is very easy to get started with Ektorp:

HttpClient httpClient = new StdHttpClient.Builder()
        .url("http://localhost:5984")
        .build();

CouchDbInstance dbInstance = new StdCouchDbInstance(httpClient);
CouchDbConnector db = new StdCouchDbConnector("mydatabase", dbInstance);

db.createDatabaseIfNotExists();

Sofa sofa = db.get(Sofa.class, "ektorp");
sofa.setColor("blue");
db.update(sofa);

Out-of-the-Box CRUD

Ektorp features a generic repository support class. It provides all Create, Read, Update and Delete operations for a persistent class.

Here's how a SofaRepository implemented with the generic repository looks like

public class SofaRepository extends CouchDbRepositorySupport<Sofa> {

    public SofaRepository(CouchDbConnector db) {
        super(Sofa.class, db);
    }

}

This repository will have the following methods "out of the box":

SofaRepository repo = new SofaRepository(db);

repo.add(Sofa s);
repo.contains("doc_id");
Sofa sofa = repo.get("doc_id");
repo.update(Sofa s);
repo.remove(Sofa s);
List<Sofa> repo.getAll();

Convenient Management of View Definitions

The concept of views in CouchDB can be a little daunting at first and there will always be the task of managing view definitions to go along your mapped classes. Ektorp provides two solutions for this:

Embedded View Definitions

It is possible to embed view definitions in your repository classes through a @View annotation:

@View( name="complicated_view", file = "complicated_view.json")
public class BlogPostRepository extends CouchDbRepositorySupport<BlogPost> {

    @Autowired
    public BlogPostRepository(@Qualifier("blogPostDatabase") CouchDbConnector db) {
        super(BlogPost.class, db);
        initStandardDesignDocument();
    }

    @Override
    @View( name="all", map = "function(doc) { if (doc.title) { emit(doc.dateCreated, doc._id) } }")
    public List<BlogPost> getAll() {
        ViewQuery q = createQuery("all").descending(true);
        return db.queryView(q, BlogPost.class);
    }

    @GenerateView
    public List<BlogPost> findByTag(String tag) {
        return queryView("by_tag", tag);
    }

}

Automatic view generation for finder methods

Finder methods annotated with @GenerateView will have their view definitions automatically created. CouchDbRepositorySupport will generate a "by_tag" view in CouchDB at application start up for the method "findByTag" in the example above.

Simple and Powerful JSON / Object Mapping

The JSON / Object mapping in Ektorp is handled by the excellent Jackson JSON library.

Jackson makes it easy to map the common cases and provides for instance the possibility to map polymorph types for more advanced use cases.

All persistent objects managed by Ektorp need to define properties for id and revision and they need to be accessible by getters and setters.

Here's an trivial example class:

import org.codehaus.jackson.annotate.*;

@JsonWriteNullProperties(false)
@JsonIgnoreProperties({"id", "revision"})
public class Sofa {

    @JsonProperty("_id")
    private String id;

    @JsonProperty("_rev")
    private String revision;

    private String color;

    public void setId(String s) {
        id = s;
    }

    public String getId() {
        return id;
    }

    public String getRevision() {
        return revision;
    }

    public void setColor(String s) {
        color = s;
    }

    public String getColor() {
        return color;
    }
}

Querying Views

There are several methods for querying CouchDB views from Ektorp.

Query for Objects

If the view's result value field is a document, Ektorp can load the result as a List of Objects

ViewQuery query = new ViewQuery()
        .designDocId("_design/Sofa")
        .viewName("by_color")
        .key("red");

List<Sofa> redSofas = db.queryView(query, Sofa.class);

Scalar queries

It is possible to query for scalar values. Currently just String and int values are supported.

ViewQuery query = new ViewQuery()
        .designDocId("_design/somedoc")
        .viewName("some_view_name");

ViewResult result = db.queryView(query);
for (ViewResult.Row row : result.getRows()) {
    String stringValue = row.getValue();
    int intValue = row.getValueAsInt();
}

It is of course possible to parse a string value as JSON. View Result as Raw JSON Stream

The most flexible method is query for stream. The result is returned as a stream.

ViewQuery query = new ViewQuery()
        .designDocId("_design/somedoc")
        .viewName("view_with_huge_result");

InputStream data = db.queryForStream(query);
// ...
data.close();

Try it Out

Download binaries from maven repository

If you are using Maven:

<dependency>
    <groupId>org.ektorp</groupId>
    <artifactId>org.ektorp</artifactId>
    <version>1.4.4</version>
</dependency>

Getting Help

You can usually get quick answers at the Ektorp google group

ektorp's People

Contributors

beilharz avatar ceefour avatar dadoonet avatar devnied avatar dlvenable avatar dongshengbc avatar grove avatar huxi avatar jaakkosipari avatar mihxil avatar mschoch avatar n3integration avatar npetzall avatar paolobazzi avatar pasieronen avatar pdeschen avatar phedny avatar pimterry avatar rinverardi avatar rwitzel avatar seanadkinson avatar sghill avatar sparkle-sparkle avatar thesverre avatar tinexw avatar vladislav-fedotov avatar willholley avatar williamd1618 avatar yannrobert avatar yaronyg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ektorp's Issues

add @UpdateHandler annotation

CouchDB update handlers should be able to be defined in @UpdateHandler annotations in the same way as @show, @list and @filter functions work.

Modify org.ektorp.support.StdDesignDocumentFactory so it can handle the new annotation

StdObjectMapperFactory Bug

In my application I need to register some MixIn modules to deal with 3rd party library POJO's that do not have a no arg constructor. I created a jackson ObjectMapper and registered my modules. I then created an instance of StdObjectMapperFactory and used it's setObjectMapper to put my instance of ObjectMapper into the factory. I then pass the factory to the StdCouchDbConnector constructor.

createObjectMapper works just like you would expect and passes the ObjectMapper I have given the class back out.

public synchronized ObjectMapper createObjectMapper() {
    if (instance == null) {
        instance = new ObjectMapper();
        applyDefaultConfiguration(instance);
    }
    return instance;
}

However, the StdObjectMapperFactory.createObjectMapper(CouchDbConnector) method ignores the existence of the "instance" of ObjectMapper I gave it.

public ObjectMapper createObjectMapper(CouchDbConnector connector) {
    ObjectMapper objectMapper = new ObjectMapper();
    applyDefaultConfiguration(objectMapper);
    objectMapper.registerModule(new EktorpJacksonModule(connector, objectMapper));
    return objectMapper;
}

Looks to me like the to lines

    ObjectMapper objectMapper = new ObjectMapper();
    applyDefaultConfiguration(objectMapper);

should be replaced with something like

        ObjectMapper objectMapper = createObjectMapper();

Thanks in advance!

Ektorp Android - NetworkOnMainThreadException from ChangesFeedAsyncTask

On android, creating a CouchDbConnector on UI thread throws a NetworkOnMainThreadException error while the device is running in StrictMode(android treats this as a network call). So, it has to be created in a background thread.

Currently, the ChangesFeedAsyncTask class takes a CouchDbConnector as one of the Constructor argument and because AsyncTasks are instantiated from UI thread, the CouchDBConnector has to be first obtained in a background thread and then be used for instantiating ChangesFeedAsyncTask. Instead of letting applications spawn a separate thread just for creating a connector, it would be easier if the ChangesFeedAsyncTask is changed as :

/** 
 * Create a ChangesFeedAsynTask with ChangesCommand 
 * 
 * @param changesCommand the changeCommand to execute 
 */ 
public ChangesFeedAsyncTask(ChangesCommand changesCommand) { 
this.changesCommand = changesCommand; 

} 

@Override 
protected Object doInBackground(Void... params) { 
 Object result = null; 
 couchDbConnector = getCouchDbConnector(); 
 changesFeed = couchDbConnector.changesFeed(changesCommand); 
 ... 
 ... 
 } 

 // override this method to return a CouchDbConnector 
protected abstract CouchDbConnector getCouchDbConnector(); 

ensureFullCommit leaks connections

StdCouchDbConnector.ensureFullCommit uses the wrong post method in RestTemplate which causes a connection leak.

This is fixed by calling a post method that will auto relase resources.

JSONEncoding does not encode string with double quote correctly

If you attempt to query a view with key

keycontaining"doublequote

JSONEncoding.jsonEncode does not escape the double quote resulting in invalid json, so couchdb rejects the key.

A failing test to show the issue:

assertEquals("\"string\\\"withquote\"", JSONEncoding.jsonEncode("string\"withquote"));

When calling:

ViewQuery query = new ViewQuery().designDocId("_design/CouchProfile").viewName("by_username").key("keycontaining\"doublequote").includeDocs(true);
return db.queryView(query, CouchProfile.class);

You get the stack trace:

org.ektorp.DbAccessException: 400:Bad Request
URI: /oogway/_design/CouchProfile/_view/by_username?key=%22KEYCONTAINING%22DOUBLEQUOTE%22&include_docs=true
Response Body: 
{
  "error" : "bad_request",
  "reason" : "invalid UTF-8 JSON: \"\\\"KEYCONTAINING\\\"DOUBLEQUOTE\\\"\""
}
2011-09-08 11:20:24,273 [http-8080-3-13] ERROR s.o.e.mapper.GenericExceptionMapper - Unhandled exception
org.ektorp.DbAccessException: 400:Bad Request
URI: /oogway/_design/CouchProfile/_view/by_username?key=%22KEYCONTAINING%22DOUBLEQUOTE%22&include_docs=true
Response Body: 
{
  "error" : "bad_request",
  "reason" : "invalid UTF-8 JSON: \"\\\"KEYCONTAINING\\\"DOUBLEQUOTE\\\"\""
}
    at org.ektorp.http.StdResponseHandler.createDbAccessException(StdResponseHandler.java:44) ~[org.ektorp-1.2.1.jar:na]
    at org.ektorp.http.StdResponseHandler.error(StdResponseHandler.java:62) ~[org.ektorp-1.2.1.jar:na]
    at org.ektorp.http.RestTemplate.handleResponse(RestTemplate.java:98) ~[org.ektorp-1.2.1.jar:na]
    at org.ektorp.http.RestTemplate.get(RestTemplate.java:22) ~[org.ektorp-1.2.1.jar:na]
    at org.ektorp.impl.StdCouchDbConnector.queryView(StdCouchDbConnector.java:356) ~[org.ektorp-1.2.1.jar:na]

Changelog should list dependency changes

Not everyone uses Maven in all projects, so it'd be helpful to list changed dependencies in Changelog. It seems 1.2.0 added at least httpclient-cache and commons-beanutils, and requires a newer version of Jackson than 1.1.1...

Make CouchDbRepositorySupport more resilient

It should be possible to instantiate a repository based on CouchDbRepositorySupport even though the backing CouchDB instance is not available at the time.

currently, the constructor makes a call to db.createDbIfNotExists wich will cause an excption to be thrown during creation.

Docs with jquery.couch.attachPrevRev true to be versioned on save

To try and keep ektorp and jquery.couch.js doing the same thing, have ektorp add an attachment of the version doc on a save.

The versioning code can also be invoked by adding a flag to the document. Currently setting the field
doc["jquery.couch.attachPrevRev"] = true will cause jquery.couch.js to start versioning the document the next time it is
opened. > Thanks for the suggestion, Damien, this was simple to implement and makes it easier for end users to make
use of the > feature, even just in Futon.

See "The Approch" in this doc http://jchrisa.net/drl/_design/sofa/_list/post/post-page?startkey=%5B%22Versioning-docs-in-CouchDB%22%5D

And here:
https://github.com/apache/couchdb/blob/trunk/share/www/script/jquery.couch.js#L608

Default url overwrites host and port in spring config

HttpClientFactoryBean sets a default url which will be passed to StdHttpClient.Builder. Since StdHttpClient.Builder.url() overrides host and port unless the url is null, it's not possible to use separate host and port parameters unless the url really is set to null via HttpClientFactoryBean.setProperties or HttpClientFactoryBean.setUrl.

using attachments from couchDB with ektorp

I would be very interested to know how to use attachments with ektorp

I use this code:

MenuButtonHeader mb = db.get(MenuButtonHeader.class, doc);
System.out.println(mb.getAttachments());
Collection attachmentKeySet = mb.getAttachments().values();
Attachment att = null;
for(Attachment a : attachmentKeySet){
att = a;
}
System.out.println(att.getContentLength());
System.out.println(att.getContentType());
System.out.println(att.getId());
System.out.println(att.isStub());

System.out.println(att.getDataBase64().length()); -----------> (NPE)

and get this output:
{menu-1.png=org.ektorp.Attachment@69d6065}
2094
image/png
null
true

The ID of my attachment is null, and the data DataBase64 is null.
I've read the documentation from http://www.ektorp.org/reference_documentation.html#d0e909 but it hasn't been able to help me solve my problem of loading an attachment from couchdb into java.

thanks

Upgrade Jackson to 1.9 to support @JsonUnwrap

@JsonUnwrap is the most voted feature request for Jackson 1.9. It's very useful for many projects.

However, the current version of Ektorp only references 1.8.5. I tried to upgrade to 1.9 and get the following error:

java.lang.NoSuchMethodError: org.codehaus.jackson.map.DeserializationConfig.isEnabled

Seems like Jackson has made some breaking changes to 1.9.

Here is some context for this feature request:
http://jira.codehaus.org/browse/JACKSON-132

HTTP errors leak StreamingJsonSerializer writer threads

If StdCouchDbConnector.executeBulk fails due to e.g. "connection refused" exception (from RestTemplate), and the bulk operation is >1024 bytes in length (PipedInputStream's default buffer size), the writer thread/task (created in StreamingJsonSerializer.createBulkOperation) never finishes (so it accumulates threads without an upper bound). Furthermore, the writer thread is not a daemon thread, so it also prevents JVM exit.

the first query always fails after a CouchDbConnector created

I'm using ektorp-1.2.1.

I created a dbInstacne and using several CouchDbConnectors with this CouchDbInstance.

for the first few times, it works fine.
but at one point, the first query fails after a CouchDbConnector created.

here's the code summary


HttpClient httpClient = new StdHttpClient.Builder().connectionTimeout(0).socketTimeout(0).url(address).build();

CouchDbInstance dbInstance = new StdCouchDbInstance(httpClient);

CouchDbConnector dbCon = new StdCouchDbConnector("db_A", dbInstance);
//using dbCon for query.... (it works)

dbCon = new StdCouchDbConnector("db_B", dbInstance);
//using dbCon for query.... (it works)

dbCon = new StdCouchDbConnector("db_C", dbInstance);
//using dbCon for query.... (it works)

// i used same dbCon value as different connectors with a same dbInstance.

//FIXME : at some point, it won't work
dbCon = new StdCouchDbConnector("db_A", dbInstance);
ViewQuery query = new ViewQuery().designDocId("_design/A").viewName("viewName_A").key("keyA");
try {
ViewResult result = dbCon.queryView(query); // it throws an "SocketTimeoutException: Read timed out" exception.
} catch(Exception e){}

ViewResult result = dbCon.queryView(query); // now it works...


should i created different CouchDbInstance for different db?
or is this a bug?

XML-namespace support for Spring

It should be possible to declare connectors in an application xml like this:

<couchdb:connector database="myDatabase" />

It should be possible to define boostrap data locations:

<couchdb:connector database="myDatabase">
<couchdb:bootstrap location="classpath:myBoostrapData.json">
/couchdb:connector

avoid dependency to commons-beanutils

in HttpClientFactoryBean, using "new DirectFieldAccessor(this).setPropertyValues(couchDBProperties);" instead of BeanUtils.populate would avoid an extra dependency

DocumentReferences testing can fail if field is boolean

When using DocumentReferences the generated view tests for a boolean field to determine the document type

for example:

@TypeDiscriminator
private Boolean moduleEnabled;
private String moduleParent;

@DocumentReferences(fetch = FetchType.LAZY, descendingSortOrder = true, orderBy = "_id", backReference = "moduleParent")
    private Set modules;

with

@GenerateView
    public List findByModuleParent(String moduleParent) {
        return queryView("by_moduleParent", moduleParent);
    }


this generated:

function(doc) { if(doc.moduleEnabled && doc.moduleParent) { emit([doc.moduleParent, 'modules', doc._id], null); } }

this test fails with doc.moduleEnabled set to false, I believe it would be a good idea if a custom document validator could be set?

Thanks for such a good library!

Francis

Support Document Copy

Couch DB supports a copy operation, which is handy for docs with lots of attachments. Here is a sample implementation:

public HttpResponse copyDoc(org.apache.http.client.HttpClient apacheClient, String dbName, String fromDocID, Revision rev, String toDocID) {
    //RestTemplate restTemplate = new RestTemplate(dbInstance.getConnection());

    try {
        BasicRequestLine requestLine = new BasicRequestLine("COPY", "/" + dbName + "/" + fromDocID, HttpVersion.HTTP_1_1);
        BasicHttpRequest request = new BasicHttpRequest(requestLine);
        String dest = toDocID;
        if (rev != null) {
            dest += "?rev=" + rev.getRev();
        }

        request.addHeader("Destination", dest);
        org.apache.http.HttpResponse response = apacheClient.execute((HttpHost)apacheClient.getParams().getParameter(ClientPNames.DEFAULT_HOST), request);
        return null; // bad bad BAD!!!! need
    } catch (Exception e) {
        throw Exceptions.propagate(e);
    }
}

"Expected data to start with an Object" RuntimeException on list function

My application uses a list function to transform a view. Works perfectly.

However the result does not have the header fields that QueryResultParser seems to expect; i.e. instead of:

{"total_rows":2,"offset":1,"rows":[
    {"id":"doc_id1","key":"key_value","value":"doc_value1"},
    {"id":"doc_id2","key":"key_value","value":"doc_value2"}
]}

the request returns:

[
    {"id":"doc_id1","key":"key_value","value":"doc_value1"},
    {"id":"doc_id2","key":"key_value","value":"doc_value2"}
]

I'd like to modify QueryResultParser.parseResult to skip over the header fields if they aren't present. Just wanted to make sure I wasn't off in the weeds before doing it though.

NullPointerException (when database is unavailable)

Caused by: java.lang.NullPointerException
at java.io.FilterInputStream.read(FilterInputStream.java:116)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
at java.io.InputStreamReader.read(InputStreamReader.java:167)
at java.io.Reader.read(Reader.java:123)
at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:1364)
at org.apache.commons.io.IOUtils.copy(IOUtils.java:1340)
at org.apache.commons.io.IOUtils.copy(IOUtils.java:1287)
at org.apache.commons.io.IOUtils.toString(IOUtils.java:502)
at org.ektorp.http.StdResponseHandler.createDbAccessException(StdResponseHandler.java:28)
at org.ektorp.http.StdResponseHandler.error(StdResponseHandler.java:62)
at org.ektorp.http.RestTemplate.handleResponse(RestTemplate.java:110)
at org.ektorp.http.RestTemplate.get(RestTemplate.java:22)
at org.ektorp.impl.StdCouchDbConnector.get(StdCouchDbConnector.java:237)
at org.ektorp.impl.StdCouchDbConnector.get(StdCouchDbConnector.java:228)

Minor. This is in version 1.3.0

Better error reporting

It should be possible to determine if an DbAccessException was caused by the client or the server.
If couchdb responds with http status in the 400 it is a client error, 500 is a server error.

This could be reflected by inserting new types in the exception hierarchy:
ClientDbAccessException
ServerDbAccessException

It is important that a network error easily can be distinguished from an exception caused by wrong usage of the API

Runtime implemetation of repository interfaces

It is possible for a framework like Ektorp to dynamically create an implementation of a repository interface.

The benefit is that a developer has to write less code and hence less bugs.

Views can be generated based on name convetions and annotations.

Example:

// Look ma, no implementation!
MyRepoInterface r = ektorpRepostoryFactory.createRepo(MyRepoInterface.class);
r.add(mySofa);

The actual implementation of this feature can be solved through JDK dynamic proxies.

Support for dirty tracking

Sorry, I created this on google code already. It would be helpful to have dirty tracking on objects, so that an extra save to couch could be avoided; reducing wasted revision changes.

Wrong use of total_rows field in QueryResultParser

QueryResultParser uses the total_rows field in view to pre-allocate the result list.
This is clearly wrong as this field does not reflect the number of rows in the reuslt, but number of rows in the view.

This can be a problem if the database contains large amounts of documents as out of memory errors easily can occur.

@GenerateView not working as expected

Hi, it seems we have come across some inconsistent functionality when using ektorp 1.2.1 with different versions of couchdb. See below for description.

What steps will reproduce the problem?

  1. Create Domain Object
  2. fetch it from DB and update
  3. Fetch again

What is the expected output? What do you see instead?
Expected = new (updated) values; actual = inital values

What version of the product are you using? On what operating system?
Ektorp 1.2.1 on Win XP SP3

Today I upgraded from couchdb 0.11.2 to 1.1.0. When I run my unit tests, and re-fetch my domain after an update - it still returns the initial value. This seems peculiar to ektorp since futon shows the updated values. If I change back to the older version of couchdb however, I get the green light.

I use the following format (not full implementation but shows relevant code):

public List<DomainObjName> findMyDomainObjName(String key) {
 return queryView("by_domainobjname", key);
}

Update: The problem seems to be with @generated. If we replace this with our own view and use:

ViewQuery q = createQuery("my view").descending(true);
List<DomainObjName> list = db.queryView(q, DomainObjName.class);

...then this works as expected.

Abhaya

Fetch attachments from previous revisions

It is possible to fetch attachments from previous revisions in couchdb:
http://localhost:5984/dbName/docId/attachmentName?rev=revisionId

It would be nice to have an overloaded version of the getAttachment method, something like:

    public AttachmentInputStream getAttachment(final String id, final String attachmentId, final String rev) {
            assertDocIdHasValue(id);
            Assert.hasText(attachmentId, "attachmentId must have a value");
                    Assert.hasText(rev, "Revision may not be null or empty");
            if (LOG.isTraceEnabled()) {
                LOG.trace("fetching attachment for doc: {} attachmentId: {}", id,
                        attachmentId);
            }

            HttpResponse r = restTemplate.get(dbURI.append(id).append(attachmentId).param("rev", rev)
                    .toString());
            return new AttachmentInputStream(attachmentId, r.getContent(),
                    r.getContentType(), r.getContentLength());
    }

Issues with paging backwards

Using 1.2.2

  1. Cannot page back to page 1.
  2. Using links behaves differently from using the page request directly.

This sample code illustrates how page 2 repeats again instead of paging back to page 1

  public void simpleTest1() {
try {
  ViewQuery query = new ViewQuery();
  query.viewName("byUserName");
  query.designDocId("_design/MyDocument");
  query.key("client_a");

  CouchDbConnector dbConnector = new StdCouchDbConnector("mydb", new StdCouchDbInstance(new StdHttpClient.Builder().url("http://localhost:5984").build()));

  PageRequest pageRequest = PageRequest.firstPage(5);

  Page<JsonNode> page = dbConnector.queryForPage(query, pageRequest, JsonNode.class);
  List<JsonNode> list = page.getRows();
  System.out.println("Page 1");
  for (JsonNode node : list) {
    System.out.println(node);
  }

  page = dbConnector.queryForPage(query, page.getNextPageRequest(), JsonNode.class);
  list = page.getRows();
  System.out.println("Page 2");
  for (JsonNode node : list) {
    System.out.println(node);
  }

  page = dbConnector.queryForPage(query, page.getNextPageRequest(), JsonNode.class);
  list = page.getRows();
  System.out.println("Page 3");
  for (JsonNode node : list) {
    System.out.println(node);
  }

  page = dbConnector.queryForPage(query, page.getNextPageRequest(), JsonNode.class);
  list = page.getRows();
  System.out.println("Page 4");
  for (JsonNode node : list) {
    System.out.println(node);
  }

  page = dbConnector.queryForPage(query, page.getPreviousPageRequest(), JsonNode.class);
  list = page.getRows();
  System.out.println("Page 3");
  for (JsonNode node : list) {
    System.out.println(node);
  }

  page = dbConnector.queryForPage(query, page.getPreviousPageRequest(), JsonNode.class);
  list = page.getRows();
  System.out.println("Page 2");
  for (JsonNode node : list) {
    System.out.println(node);
  }

  page = dbConnector.queryForPage(query, page.getPreviousPageRequest(), JsonNode.class);
  list = page.getRows();
  System.out.println("Page 1");
  for (JsonNode node : list) {
    System.out.println(node);
  }

} catch (Exception e) {
  e.printStackTrace();
}
    }

OUTPUTS:
Page 1
{"_id":"1329759612408"}
{"_id":"1329759622144"}
{"_id":"1329759622300"}
{"_id":"1329759622456"}
{"_id":"1329759622612"}
Page 2
{"_id":"1329759622752"}
{"_id":"1329759622908"}
{"_id":"1329759623064"}
{"_id":"1329759623252"}
{"_id":"1329759623361"}
Page 3
{"_id":"1329759623517"}
{"_id":"1329759623658"}
{"_id":"1329759623829"}
{"_id":"1329759624345"}
{"_id":"1329759624532"}
Page 4
{"_id":"1329759624704"}
{"_id":"1329759624876"}
{"_id":"1329759625032"}
Page 3
{"_id":"1329759623517"}
{"_id":"1329759623658"}
{"_id":"1329759623829"}
{"_id":"1329759624345"}
{"_id":"1329759624532"}
Page 2
{"_id":"1329759622752"}
{"_id":"1329759622908"}
{"_id":"1329759623064"}
{"_id":"1329759623252"}
{"_id":"1329759623361"}
Page 1
{"_id":"1329759622752"} //REPEAT ISSUE
{"_id":"1329759622908"}
{"_id":"1329759623064"}
{"_id":"1329759623252"}
{"_id":"1329759623361"}

This sample code illustrates how using links while back paging does not return pages in same order as forward paging:

    public void simpleTest2() {
try {
  ViewQuery query = new ViewQuery();
  query.viewName("byUserName");
  query.designDocId("_design/MyDocument");
  query.key("client_a");

  CouchDbConnector dbConnector = new StdCouchDbConnector("mydb", new StdCouchDbInstance(new StdHttpClient.Builder().url("http://localhost:5984").build()));

  PageRequest pageRequest = PageRequest.firstPage(5);

  Page<JsonNode> page = dbConnector.queryForPage(query, pageRequest, JsonNode.class);
  List<JsonNode> list = page.getRows();
  System.out.println("Page 1");
  for (JsonNode node : list) {
    System.out.println(node);
  }

  page = dbConnector.queryForPage(query, PageRequest.fromLink(page.getNextPageRequest().asLink()), JsonNode.class);
  list = page.getRows();
  System.out.println("Page 2");
  for (JsonNode node : list) {
    System.out.println(node);
  }

  page = dbConnector.queryForPage(query, PageRequest.fromLink(page.getNextPageRequest().asLink()), JsonNode.class);
  list = page.getRows();
  System.out.println("Page 3");
  for (JsonNode node : list) {
    System.out.println(node);
  }

  page = dbConnector.queryForPage(query, PageRequest.fromLink(page.getNextPageRequest().asLink()), JsonNode.class);
  list = page.getRows();
  System.out.println("Page 4");
  for (JsonNode node : list) {
    System.out.println(node);
  }

  page = dbConnector.queryForPage(query, PageRequest.fromLink(page.getPreviousPageRequest().asLink()), JsonNode.class);
  list = page.getRows();
  System.out.println("Page 3");
  for (JsonNode node : list) {
    System.out.println(node);
  }

  page = dbConnector.queryForPage(query, PageRequest.fromLink(page.getPreviousPageRequest().asLink()), JsonNode.class);
  list = page.getRows();
  System.out.println("Page 2");
  for (JsonNode node : list) {
    System.out.println(node);
  }

  page = dbConnector.queryForPage(query, PageRequest.fromLink(page.getPreviousPageRequest().asLink()), JsonNode.class);
  list = page.getRows();
  System.out.println("Page 1");
  for (JsonNode node : list) {
    System.out.println(node);
  }

} catch (Exception e) {
  e.printStackTrace();
}
    }

OUTPUTS:
Page 1
{"_id":"1329759612408"}
{"_id":"1329759622144"}
{"_id":"1329759622300"}
{"_id":"1329759622456"}
{"_id":"1329759622612"}
Page 2
{"_id":"1329759622752"}
{"_id":"1329759622908"}
{"_id":"1329759623064"}
{"_id":"1329759623252"}
{"_id":"1329759623361"}
Page 3
{"_id":"1329759623517"}
{"_id":"1329759623658"}
{"_id":"1329759623829"}
{"_id":"1329759624345"}
{"_id":"1329759624532"}
Page 4
{"_id":"1329759624704"}
{"_id":"1329759624876"}
{"_id":"1329759625032"}
Page 3
{"_id":"1329759622752"} //DIFFERS FROM PAGE 3 BEFORE
{"_id":"1329759622908"}
{"_id":"1329759623064"}
{"_id":"1329759623252"}
{"_id":"1329759623361"}
Page 2
{"_id":"1329759623517"} //DIFFERS FROM PAGE 2 BEFORE
{"_id":"1329759623658"}
{"_id":"1329759623829"}
{"_id":"1329759624345"}
{"_id":"1329759624532"}
Page 1
{"_id":"1329759623517"} //STILL HAS REPEAT ISSUE
{"_id":"1329759623658"}
{"_id":"1329759623829"}
{"_id":"1329759624345"}
{"_id":"1329759624532"}

Compatibility with Jackson 1.8

Classes in org.ektorp.impl.docref extends and make use of internal Jackson classes. Some of these has changed in Jackson 1.8 and Ektorp doesn't compile with 1.8.

Generic DocumentAccessor?

I saw that DocumentAccessor was no longer generic. I remember doing a patch for that some months ago and that it had been included (back in google code). I had a couple of classes implementing it and they no longer compile (due to no longer being generic). I found it so much easier (and safer) to implement custom DocumentAccessor when it was generic :(

I saw the commit that removed the generics, and it said "unnecessary complexity". The code seemed rather clean and understandable (from my point of view) and removed complexity for the user, so I was just wondering what was complex in the implementation? Maybe if I knew what's wrong I could provide a simpler patch?

getDB() from CouchDbRepositorySupport

Hi,

I need to get the attachment from a document and for that I understand I should use something like:

documentRepository.getDb().getAttachment(id, attachmentId);

However getDb() does not exist and db member is not accessible. What would be the recommended way to get the attachment?

Thanks!
-Nestor

Feature request: custom design document names for repositories

Currently design documents used with repositories must be named after the repository type simple name.

It would be very helpful to be able to specify a design document to use with the repository that is not linked to the repository type, either by way of a class attribute or alternate constructor.

Please consider adding the ability to specify a custom design document name for use with repositories.

Range Requests For Attachments

Since 1.1.0 couch supports range requests for attachements.

I have a need to access attachments with a range request, but the attachment input stream exposes no way to do such. It would be great to request from the connector an InputSteam that had the ability to seek, using the underlying range requests.

Secure vs cleartext url leads to: org.ektorp.DocumentNotFoundException

If I configure my client using a cleartext (http) url:

            // create the client
            org.ektorp.http.HttpClient httpClient = new StdHttpClient.Builder()
            .url("http://my.couchdb.com:80")
            .username("user")
            .password("pwd")
            .build();

then everything works.

BUT if I try to use a secure (https) URL like so:

            // create the client
            org.ektorp.http.HttpClient httpClient = new StdHttpClient.Builder()
            .url("https://my.couchdb.com:443")
            .username("user")
            .password("pwd")
            .relaxedSSLSettings(true)
            .build();

Then I get an exception stating:
org.ektorp.DocumentNotFoundException: nothing found on db path: ..., Response body: null

I tried 1.2.1, 1.2.2 and 1.3.0 with all of them yielding the same results.

I've attached the snapshot for the dependency hierarchy from my pom.xml file, if that makes any difference.

BTW I also tried this piece of code and it yields the same error:

            // create the client
            org.ektorp.http.HttpClient httpClient = new StdHttpClient.Builder()
            .host("my.couchdb.com")
            .port(443)
            .username("user")
            .password("pwd")
            .relaxedSSLSettings(true)
            .build();

Multiple Ektorps Race Condition with Design Docs

It appears that if you have multiple different Ektorps running in multiple separate processes (such as on a compute grid) against the same CouchDB server ....

If you are using CouchDbRepositorySupport, a race condition exists in which threads from the separate processes all try to create the same design doc for the first time, at the same time.

You could catch UCE's and use the one which was inserted before you succeeded (first).

Exception in thread "main" org.ektorp.UpdateConflictException: document update conflict: id: _design/MySecretClass rev: null
        at org.ektorp.impl.StdCouchDbConnector$6.error(StdCouchDbConnector.java:235)
        at org.ektorp.impl.StdCouchDbConnector$6.error(StdCouchDbConnector.java:224)
        at org.ektorp.http.RestTemplate.handleResponse(RestTemplate.java:90)
        at org.ektorp.http.RestTemplate.put(RestTemplate.java:34)
        at org.ektorp.impl.StdCouchDbConnector.update(StdCouchDbConnector.java:223)
        at org.ektorp.support.CouchDbRepositorySupport.initStandardDesignDocument(CouchDbRepositorySupport.java:246)

avoid runtime dependency to httpclient-cache if not using caching

Currently Ektorp requires httpclient-cache jar file even if you disable caching. This could be easily avoided by moving caching initialization (in StdHttpClient) to an inner class:

    // separate class to avoid runtime dependency to httpclient-cache unless using caching
private static class WithCachingBuilder {
    public static org.apache.http.client.HttpClient withCaching(org.apache.http.client.HttpClient client, int maxCacheEntries, int maxObjectSizeBytes) {
        CacheConfig cacheConfig = new CacheConfig();  
        cacheConfig.setMaxCacheEntries(maxCacheEntries);
        cacheConfig.setMaxObjectSizeBytes(maxObjectSizeBytes);
        return new CachingHttpClient(client, cacheConfig);
    }
}

Called like this:

        if (caching) {
            cachingHttpClient = WithCachingBuilder.withCaching(client, maxCacheEntries, maxObjectSizeBytes);
        }

(The compile-time dependency is still there, of course.)

Move DesignDocument instance creation to a separate method in StdDesignDocumentFactory

I am using ektorp with couchdb-lucene, so I need to generate fulltext fields for design documents. I extended DesignDocument to add this field, but currently there's no clean way to pass that instance to that the StdDesignDocumentFactory. So instead of invoking [code]
DesignDocument dd = new DesignDocument()
[/code]

inside generateFrom(metadata) method, add a method to create it, something along the lines:

[code]
public DesignDocument createDesignDocumentInstance() {
return new DesignDocument();
}
[/code]

adding attachments to a CouchDbDocument

Hi,

Great API. Would it make sense to make addInlineAttachment() public method? For now I made a copy of CouchDbDocument to solve my road block.

Thanks!
-Nestor

ReplicationTo Cloudant Fails

I assume that cloudant is using a non standart source_last_seq, but since they are a big hosting provider of CouchDB, this is a bit of an issue. Here is the stacktrace which should explain.

org.ektorp.DbAccessException: org.codehaus.jackson.map.JsonMappingException: Can not construct instance of int from String value '14-g1AAAAB1eJzLYWBgYMpgTmFQSElKzi9KdUhJMtHLTS3KLElMT9VLzskvTUnMK9HLSy3JAalMZEiq____f1YiB6oeIzx68liAJEMDkPoP1sqWBQCmxicY': not a valid Integer value
at [Source: org.ektorp.http.StdHttpResponse$ConnectionReleasingInputStream@3aca5e2; line: 1, column: 59](through reference chain: org.ektorp.ReplicationStatus["source_last_seq"])
at org.ektorp.util.Exceptions.propagate(Exceptions.java:19)
at org.ektorp.http.RestTemplate.handleResponse(RestTemplate.java:92)
at org.ektorp.http.RestTemplate.post(RestTemplate.java:53)
at org.ektorp.impl.StdCouchDbInstance.replicate(StdCouchDbInstance.java:71)
at org.ektorp.impl.StdCouchDbConnector.replicateTo(StdCouchDbConnector.java:369)
at com.googlecode.reupholster.Promote.promote(Promote.java:66)
at com.googlecode.reupholster.Promote.promote(Promote.java:41)
at com.googlecode.reupholster.App.deploy(App.java:698)
at com.googlecode.reupholster.App$6.actionPerformed(App.java:685)
at java.awt.MenuItem.processActionEvent(MenuItem.java:627)
at java.awt.MenuItem.processEvent(MenuItem.java:586)
at java.awt.MenuComponent.dispatchEventImpl(MenuComponent.java:317)
at java.awt.MenuComponent.dispatchEvent(MenuComponent.java:305)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:638)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:296)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:211)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:196)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:188)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

Connection hangs with incorrect credentials

When you set up a HttpClientFactoryBean with a username and password, if the username / password combination is incorrect then the client will hang (infinite loop in HttpClient) when trying to access a document instead of throwing out the 401 Unauthorized HTTP status.

Obviously in a properties file this is an unrecoverable application config error, however we want to use the basic authentication that is inbuilt in couchdb and hence wish to attempt to gain access to a document using HttpClientFactoryBean to build a client using the users credentials from a logon form.. if the user supplies the wrong password this will hang in an infinite loop.

sequence id type

The current sequence id type is String, but there is nothing that says it has to be. For example, BigCouch 0.4 uses arrays now - a change that will be rolled out to Cloudant's public clusters.

This is basically another instance of issue #44.

Could we restructure a bit so that we can be type agnostic? I'm willing to write the patch if I can get recommendations on approach - haven't lived with Ektorp long enough to make the decision myself.

Cheers.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.