Docker and Vaadin Meet Couchbase: Part I
Learn how you can fire up a web application using Spring Boot, Vaadin, and of course Couchbase (as backend) — all using Docker.
Join the DZone community and get the full member experience.
Join For FreeRunning Couchbase as a Docker container is fairly easy. Simply inherit from the latest, official Couchbase image and add your customized behavior according to your requirement. In this post, I am going to show how you can fire up a web application using Spring Boot, Vaadin, and of course Couchbase (as backend) — all using Docker.
This is part one of a two-part series where I am going to describe ways to run a fully featured web application powered by Couchbase as the NoSQL backend using Docker toolsets. In this post, I will describe the steps to set up and configure a Couchbase environment using Docker; I will also mention ways to Dockerize the web application (in this case, it’s a Spring Boot application with Vaadin) and talk to the Couchbase backend for the CRUD operations.
Prerequisites
Docker needs to be set up and working. Please refer to this link for details of the installation. If you are on macOS or Windows 10, you can go for native Docker packages. If you are on an earlier version of Windows (7 or 8) like me, then you can use Docker Toolbox which comes with Docker machine.
The Application
Ours is a simple CRUD application for maintaining a bookstore. Users of the application can add books by entering information such as title and/or author, and can then view the list of books, edit some information, and even delete the books. The app is built on Spring Boot. The backend is powered by Couchbase 4.6, and for the front-end, I have used Vaadin 7 since it has pretty neat integration with the Spring Boot framework.
The main steps to build this app are listed below:
- Run and configure Couchbase 4.6, including setting up the bucket and services using Docker.
- Build the application using Spring Boot, Vaadin, and Couchbase.
- Dockerize and run the application.
Run Couchbase 4.6 Using Docker
Check your Docker host IP. You can use:
docker-machine ip default to find out the docker_host ip address. You can also check the environment variables by doing
printenv | grep -i docker_host; it would show something like this ->
DOCKER_HOST=tcp://192.168.99.100:2376
The next task is to write the Dockerfile to run and configure Couchbase. For our application to talk to the Couchbase backend, we need to set up a bucket named “books” and also enable the index query services on the Couchbase node. The Dockerfile to all of this can be found here.
The Dockerfile uses a configuration script to set up the Couchbase node. Couchbase offers REST endpoints that can easily enable services such as querying, N1QL, and index. One can also set up buckets using these REST APIs. The configuration script can be downloaded from here.
Let’s try to build and run the Couchbase image now.
Go to the directory where the Dockerfile is.
Build the image ->
docker build -t <chakrar27>/couchbase:books .
Replace chakrar27 by your image-prefix or docker hub id.
Once the image is built, verify by doing
$ docker images
Run the image by typing:
docker run -p 8091-8093:8091-8093 -p 8094:8094 -p 11210:11210 chakrar27/couchbase:books
Sample output:
Starting Couchbase Server -- Web UI available at http://<ip>:8091 and logs available in /opt/couchbase/var/lib/couchbase/logs
Start configuring env by calling REST endpoints
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 192.168.99.100...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 127.0.0.1 (127.0.0.1) port 8091 (#0)
> POST /pools/default HTTP/1.1
> Host: 127.0.0.1:8091
> User-Agent: curl/7.49.1-DEV
> Accept: */*
> Content-Length: 55
> Content-Type: application/x-www-form-urlencoded
>
} [55 bytes data]
* upload completely sent off: 55 out of 55 bytes
< HTTP/1.1 200 OK
< Server: Couchbase Server
< Pragma: no-cache
< Date: Fri, 24 Mar 2017 03:20:51 GMT
< Content-Length: 0
< Cache-Control: no-cache
<
100 55 0 0 100 55 0 2966 --:--:-- --:--:-- --:--:-- 3666
* Connection #0 to host 127.0.0.1 left intact
* Trying 127.0.0.1...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 127.0.0.1 (127.0.0.1) port 8091 (#0)
> POST /node/controller/setupServices HTTP/1.1
> Host: 127.0.0.1:8091
> User-Agent: curl/7.49.1-DEV
> Accept: */*
> Content-Length: 32
> Content-Type: application/x-www-form-urlencoded
>
} [32 bytes data]
* upload completely sent off: 32 out of 32 bytes
< HTTP/1.1 200 OK
< Server: Couchbase Server
< Pragma: no-cache
< Date: Fri, 24 Mar 2017 03:20:56 GMT
< Content-Length: 0
< Cache-Control: no-cache
<
100 32 0 0 100 32 0 3389 --:--:-- --:--:-- --:--:-- 4000
* Connection #0 to host 127.0.0.1 left intact
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 180 100 152 100 28 8068 1486 --:--:-- --:--:-- --:--:-- 8444
HTTP/1.1 200 OK
Server: Couchbase Server
Pragma: no-cache
Date: Fri, 24 Mar 2017 03:21:01 GMT
Content-Type: application/json
Content-Length: 152
Cache-Control: no-cache
{"storageMode":"memory_optimized","indexerThreads":0,"memorySnapshotInterval":200,"stableSnapshotInterval":5000,"maxRollbackPoints":5,"logLevel":"info"}Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 127.0.0.1...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 127.0.0.1 (127.0.0.1) port 8091 (#0)
> POST /settings/web HTTP/1.1
> Host: 127.0.0.1:8091
> User-Agent: curl/7.49.1-DEV
> Accept: */*
> Content-Length: 50
> Content-Type: application/x-www-form-urlencoded
>
} [50 bytes data]
* upload completely sent off: 50 out of 50 bytes
< HTTP/1.1 200 OK
< Server: Couchbase Server
< Pragma: no-cache
< Date: Fri, 24 Mar 2017 03:21:01 GMT
< Content-Type: application/json
< Content-Length: 44
< Cache-Control: no-cache
<
{ [44 bytes data]
100 94 100 44 100 50 1554 1765 --:--:-- --:--:-- --:--:-- 2380
* Connection #0 to host 127.0.0.1 left intact
{"newBaseUri":"http://127.0.0.1:8091/"}bucket set up start
bucket name = books
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 127.0.0.1...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 127.0.0.1 (127.0.0.1) port 8091 (#0)
* Server auth using Basic with user 'Administrator'
> POST /pools/default/buckets HTTP/1.1
> Host: 127.0.0.1:8091
> Authorization: Basic QWRtaW5pc3RyYXRvcjpwYXNzd29yZA==
> User-Agent: curl/7.49.1-DEV
> Accept: */*
> Content-Length: 55
> Content-Type: application/x-www-form-urlencoded
>
} [55 bytes data]
* upload completely sent off: 55 out of 55 bytes
< HTTP/1.1 202 Accepted
< Server: Couchbase Server
< Pragma: no-cache
< Location: /pools/default/buckets/books
< Date: Fri, 24 Mar 2017 03:21:01 GMT
< Content-Length: 0
< Cache-Control: no-cache
<
100 55 0 0 100 55 0 748 --:--:-- --:--:-- --:--:-- 820
* Connection #0 to host 127.0.0.1 left intact
bucket set up done
/entrypoint.sh couchbase-server
Verify the configuration by typing http://192.168.99.100:8091 into your favorite browser.
Type Administrator as Username and password in the Password field and click Sign In.
Check the settings of the Couchbase node and verify that it is according to the configure.sh
we used above.
The bucket books.
At this point, our back-end Couchbase infrastructure is up and running. We now need to build an application that can use this backend to build something functional.
Build the Application Using Spring Boot, Vaadin, and Couchbase
Go to start.spring.io and add Couchbase as a dependency. This would place spring-data-couchbase libraries in the application classpath. Since Couchbase is considered a first-class citizen of the Spring Boot ecosystem, we can make use of the Spring Boot auto-configuration feature to access the Couchbase bucket at runtime.
Also, add Vaadin as a dependency in the project. We are going to use it for building the UI layer.
The project object model (POM) file can be found here.
We create a Couchbase repository like this:
@ViewIndexed(designDoc = "book")
@N1qlPrimaryIndexed
@N1qlSecondaryIndexed(indexName = "bookSecondaryIndex")
public interface BookStoreRepository extends CouchbasePagingAndSortingRepository<Book, Long> {
List<Book> findAll();
List<Book> findByAuthor(String author);
List<Book> findByTitleStartsWithIgnoreCase(String title);
List<Book> findByCategory(String category);
}
The annotations ensure that a View named “book” will be supplied at runtime to support view-based queries. A primary index will be created to support N1QL queries. In addition, a secondary index will also be provided.
The methods have been defined to return List<Book>. We don’t have to provide any implementation since that is already provided behind the scenes by the spring-data-couchbase.
We need to define the entity, which in our case is Book. We annotate it with @Document
.
@Document
:
public class Book {
@Id
private String id = UUID.randomUUID().toString();
private String title;
private String author;
private String isbn;
private String category;
}
To enable autoconfiguration, use application.properties
or an application.yml
file as shown below:
spring.couchbase.bootstrap-hosts=127.0.0.1
spring.couchbase.bucket.name=books
spring.couchbase.bucket.password=
spring.data.couchbase.auto-index=true
One thing to note here is that when the application container runs, it would need to connect to the Couchbase container and set up the auto-configuration. The property spring.couchbase.bootstrap-hosts lists the IP address of the Couchbase node. Here, I have specified 127.0.0.1 which is not going to work since, at runtime, the app container will not find the Couchbase container running in that IP. So we need to pass an environment variable (env variable) when running the Docker image of the application.
In order to pass an env variable as mentioned above, we need to write the Dockerfile of the application such that the value of the spring.couchbase.bootstrap-hosts property can be passed as an env variable. Here’s the Dockerfile of the app:
FROM frolvlad/alpine-oraclejdk8:full
VOLUME /tmp
ADD target/bookstore-1.0.0-SNAPSHOT.jar app.jar
RUN sh -c 'touch /app.jar'
CMD java -Dspring.couchbase.bootstrap-hosts=$HOSTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar
As you can see, we are basically overriding the value of the spring.couchbase.bootstrap-hosts property defined in the application.properties file by the env variable HOSTS.
This is pretty much all we have to do to wire Spring Boot with Couchbase.
UI (U and I)
For UI, we make use of the spring-vaadin integration. I am using version 7.7.3 of Vaadin, vaadin-spring version 1.1.0, and “Viritin,” a useful Vaadin add-on. To install Viritin, add the following dependency:
<dependency>
<groupId>org.vaadin</groupId>
<artifactId>viritin</artifactId>
<version>1.57</version>
</dependency>
Annotate the UI class as
@SpringUI
@Theme(“valo”)
public class BookstoreUI extends UI {
//////
}
And then hook the repository methods with the UI elements.
A bean that implements the CommandLineRunner interface is used to prepopulate the database with some initial values.
For full source code, refer to this link.
Dockerize the Application
Using Maven, it’s very easy to Dockerize an application using Spotify’s docker-maven plugin. Please check the pom.xml
file plugin section.
Alternatively, you can build using Docker command line ->
docker build -t chakrar27/books:standalone .
And then run the image. Note that we need to pass the value of the variable HOSTS that our app container is going to look for when it tries to connect to the Couchbase container. The run command would look like:
docker run -p 8080:8080 -e HOSTS=192.168.99.100 chakrar27/books:standalone
Once the application is started, navigate to http://192.168.99.100:8080/.
The following page shows up:
An entry can be edited and saved.
There’s also a neat filtering feature provided by the N1QL query running underneath.
Users can also add a new book and delete an existing record. All the CRUD (Create/Read/Update/Delete) features of this simple application are powered by Couchbase N1QL queries, which we enabled by creating the BookStoreRepository
, and, in turn, extends the CouchbasePagingAndSortingRepository
.
Published at DZone with permission of Laura Czajkowski, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments