Build Status: Coverage Status: Stories Ready:
dropwizard-couchbase is a Dropwizard bundle for Couchbase persistence.
The current version is 0.9.1 and it has the following dependencies:
- io.dropwizard dropwizard-core 1.1.1 (provided)
- com.couchbase.client couchbase-client 2.4.5 (compile time)
dropwizard-couchbase is compiled against JDK 8.
Add the following dependency to your build.gradle
dependencies {
compile "io.smartmachine:dropwizard-couchbase:0.9.1"
}
or pom.xml
<project>
...
<dependencies>
<dependency>
<groupId>io.smartmachine</groupId>
<artifactId>dropwizard-couchbase</artifactId>
<version>0.9.1</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
Add a CouchbaseBundle
to your Application
class:
package io.sample;
import io.dropwizard.Application;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import io.smartmachine.couchbase.CouchbaseBundle;
public class ConfigurationServer extends Application<ConfigurationServerConfig> {
public static void main(String[] args) throws Exception {
new ConfigurationServer().run(args);
}
private final CouchbaseBundle<ConfigurationServerConfig> couchbaseBundle = new CouchbaseBundle<ConfigurationServerConfig>() {
@Override
public CouchbaseClientFactory getCouchbaseClientFactory(ConfigurationServerConfig configuration) {
return configuration.getCouchbaseClientFactory();
}
};
@Override
public void initialize(Bootstrap<ConfigurationServerConfig> bootstrap) {
bootstrap.addBundle(couchbaseBundle);
}
@Override
public void run(ConfigurationServerConfig configuration, Environment environment) throws Exception {
// Register all your resources here as usual
}
@Override
public String getName() {
return "configuration-server";
}
}
Implement your configuration class.
package io.smartmachine.cs;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.dropwizard.Configuration;
import org.hibernate.validator.constraints.NotEmpty;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
class ConfigurationServerConfig extends Configuration {
// All your usual setup goes here
@Valid
@NotNull
private CouchbaseClientFactory ccf = new CouchbaseClientFactory();
@JsonProperty("couchbase")
public CouchbaseClientFactory getCouchbaseClientFactory() {
return ccf;
}
@JsonProperty("couchbase")
public void setCouchbaseClientFactory(CouchbaseClientFactory ccf) {
this.ccf = ccf;
}
}
Add the following to your yaml configuration file:
# Your server configuration
server:
applicationConnectors:
- type: http
port: 9000
adminConnectors:
- type: http
port: 9001
# Default Couchbase Configuration
couchbase:
bucket: default
hosts:
- http://localhost:8091/pools
password: ""
If you don't add a couchbase configuration section to your yaml the defaults (above) will be assumed. More CouchbaseClient configuration options will be added in the future.
dropwizard-couchbase will autogenerate DAO implementation classes and views for you to facilitate standard CRUD operations.
We assume that you have a standard dropwizard resource class as well as a model class with the appropriate JsonProperty
and JsonCreator
annotations for Jackson de/serialization. Let's look at such a class, called Device.java
:
package io.sample.api;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;
import java.util.List;
public class Device {
@Length(min = 12, max=12)
@NotEmpty
private final String serial;
private List<String> modules;
@JsonCreator
public Device(@JsonProperty("serial") String serial) {
this.serial = serial;
}
@JsonProperty
public void setModules(List<String> modules) {
this.modules = modules;
}
@JsonProperty
public List<String> getModules() {
return modules;
}
@JsonProperty
public String getSerial() {
return serial;
}
}
In order to enable automatic DAO generation you need to write an Accessor interface (similar to DAO class/interface in JPA/Hibernate):
package io.sample.api;
import io.smartmachine.couchbase.GenericAccessor;
import io.smartmachine.couchbase.CouchbaseView;
import java.util.List;
public interface DeviceAccessor extends GenericAccessor<Device> {
@ViewQuery("/^DEVICE/.test(meta.id)")
public List<Device> findAll();
}
The findall()
method will be autogenerated. The @ViewQuery
annotation will generate a Couchbase Design Document called DEVICE with a view called findAll like so:
function (doc, meta) {
if (/^DEVICE/.test(meta.id)) {
emit(meta.id, null);
}
}
The emit statement can be controlled as well, emit(meta.id, null)
is the default. The following annotation will generate a view that emits full documents: @ViewQuery("/^DEVICE/.test(meta.id)", emit = "emit(meta.id, doc)")
. Note that the name of the Design Document will be your model class .toUpperCase()
.
The extended GenericAccessor<Device>
interface has the following contract, all methods will be automatically implemented:
package io.smartmachine.couchbase;
public interface GenericAccessor<T> {
void create(String id, T newinstance);
T read(String id);
void update(String id, T object);
void delete(String id);
void set(String id, T object);
}
Note that this is CRUD plus an extra set operation to conform with the provided methods of CouchbaseClient.
The last step is to annotate your Resource class as follows:
package io.sample.resources;
import com.codahale.metrics.annotation.Timed;
import io.sample.api.Device;
import io.sample.api.DeviceAccessor;
import io.smartmachine.couchbase.Accessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.Valid;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.util.List;
@Path("/devices")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class DeviceResource {
private static final Logger log = LoggerFactory.getLogger(DeviceResource.class);
// Note the Accessor annotation
@Accessor
private DeviceAccessor accessor;
private final List<String> defaultModules;
public DeviceResource(List<String> defaultModules) {
this.defaultModules = defaultModules;
}
@GET
@Path("{id}")
@Timed
public Device devices(@PathParam("id") String id) {
return accessor.read(id);
}
@GET
@Timed
public List<Device> all() {
return accessor.findAll();
}
@PUT
@Path("{id}")
@Timed
public Device add(@PathParam("id") String id, @Valid Device device) {
if (device.getModules() == null || device.getModules().size() == 0) {
device.setModules(defaultModules);
}
accessor.create(id, device);
return device;
}
}
Note the @Accessor annotation in the above example. dropwizard-couchbase will inject an implementation of DeviceAccessor for you to use in your Resource
class.
If you want to do some more low-level stuff with CouchbaseClient
you can use the following annotation in your Resource class in stead:
import io.smartmachine.couchbase.CouchbaseClientFactory;
import io.smartmachine.couchbase.ClusterManager
public class DeviceResource {
@Accessor
private CouchbaseClientFactory factory;
@GET
@Path("{id}")
@Timed
public Device devices(@PathParam("id") String id) {
CouchbaseClient client = factory.client();
// and / or
ClusterManager manager = factory.getClusterManager();
}
...
}
- Implement full set of configuration options for CouchbaseClientFactory
- Implement proper Couchbase health checks and metrics
- Finish out unit tests
- Update Javadocs
Pull requests are very welcome. Create issues in the Github issue system for this repository against any bugs/feature requests.
dropwizard-couchbase is released under the MIT license.