Spring Boot + Spring Data Apache Cassandra + Rest API + Example

In this blog, we will learn Spring Boot integration with NoSQL database Apache Cassandra with the help of a working example, along with Python and Apache Cassandra installation steps on Windows OS.

Also, we will discuss in details – What is the Cassandra database used for? What makes Apache Cassandra unique? Who should use Cassandra? and lightweight transactions feature provided by cassandra.

First of all an overview on Application architecture.

In this article we will be exposing the Rest API for the below operations to the enduser in Spring Boot using @RestController. For Example:

/customer/save - insert records in Apache Cassandra database.
/customer/findall - fetch all the records from Apache Cassandra database.
/customer/findbyfirstname/firstname={firstname} - select a particular record from Apache Cassandra database having {firstname}.
/customer/findbyagegreaterthan/age={age} - retrieve records from Apache Cassandra database for specific cutomers having age greater than {age}.
/customer/findbyid/id={id} - retrieve records from Apache Cassandra database for specific {customer id}. E.g. id=2
/customer/deleteall - clear all the records from Apache Cassandra database permanantely.

High Level Application Architecture

Here is the high level application’s architecture with Apache Cassandra integration.

Table Of Contents
 [show]
Spring Boot Data Apache Cassandra Application
  • Apache Cassandra is a well-established wide-column NoSQL database.
  • It uses a column-based storage model to capture large amounts of unstructured data.
  • Cassandra focuses on operating in a distributed cluster of commodity servers and boasts high-availability and flexible horizontal scaling.

What is the Cassandra database used for?

Yes, it’s NoSQL etc. We all know this, but according to the question – with Cassandra, we can achieve super fast writes and reads operations in a distributed high available ecosphere, if you use it the correct way.

What makes Apache Cassandra unique?

  • It offers highly-available service and no single point of failure.
  • It can effectively and efficiently handle huge amounts of data across multiple servers. Plus, it is able to fast write huge amounts of data without affecting the read efficiency.
  • “built-for-scale” – another reason that so many organizations using Cassandra is its horizontal scalability.
  • Flexible data storage
  • Flexible data distribution
  • The properties of ACID (atomicity, consistency, isolation, and durability) are supported by Cassandra.

Who should use Cassandra?

  • If you need to store and manage large amounts of data across many servers, Cassandra could be a good solution for your business. It’s ideal for businesses who:
  • Can’t afford for data to be lost.
  • Can’t have their database down due to the outage of a single server.
  • We can design and implement cassandra for many usecase, like writing time based log’s of activities, sensor data, error logging etc. You design your schemas the way you want to query data later. After that you just throw in data, decent amounts of data.

Note: If you want to update or delete a lot of objects you should not build on Cassandra. Updates and Deletes lead to massive creation of tombstones which will lead to massive garbage collecting. This will decrease performance, especially for reads.

Apache Cassandra : Lightweight transactions ? How Come?

I would like to highlight an important finding with you.
A lightweight transactions feature – At some point Cassandra came with some new features, e.g. “lightweight transactions” right?. This sounds like a great tool at first but it comes with a big tradeoff. Think of something like “INSERT … IF NOT EXISTS” in a 100 node cluster with a quorum of 5 or so. This means your call will go round to 5 servers to check if any of them has a value righ? This is the anti-pattern we can say. Do not use this functionality! There’s SQL for such things. I don’t understand why they put time into developing this. If you have clarity on this please drop your comment in comment section, it can help others.

How to install Apache Cassandra on Windows

Note: Apache Cassandra requires Java 8 to run on a Windows OS. Additionally, the Cassandra Command-Line shell (cqlsh) is dependent on Python 2.7 to work correctly.
Hence, in this blog we will focus on

  • Download and install Python 2.7 on a Windows OS.
  • Download and install Apache Cassandra on a Windows OS.
  • Build a Spring Boot Microservice which exposes multiple RestFul operations as I mentioned above to the end user for retrieving and updating the Customer’s information in Apache Cassandra NoSQL database.

Download and install Python 2.7 on a Windows OS

We need to install Python 2.7 for cqlsh. Url : https://www.python.org/downloads/release/python-2718/

python 2.7.18 setup

Click on Next> button.

python 2.7.18 setup screen

Click on Next> button.

python 2.7.18 setup screen 2

Click on Next> button.

Installing python
complete python setup

Note: Once Python 2.7.18 installed on Windows 10 OS, we need to set environmental variables otherwise Apache Casandra cqlsh will not be prompted.

Set Environment Variable on Windows for Python 2.7

Set Environment Variable on Windows for Python 2.7
set environment variable

Download and Install Apache Cassandra on Windows

We can download Apache Cassandra from the Official website: Url: https://cassandra.apache.org/download/

Download and Install Apache Cassandra on Windows
Download and Install Apache Cassandra on Windows

Unzip the compressed tar.gz folder using a compression tool such as 7-Zip or WinZip.

Set Environment Variables for Cassandra on Windows OS

Set up the environment variables for Apache Cassandra to enable the database to interact with our application and operate on Windows as cqlsh.

Edit system variable

Start Apache Cassandra from Windows CMD prompt

Navigate to the Cassandra bin folder. Start the Windows Command Prompt directly from within the bin folder by typing cmd in the address bar and pressing Enter.

Start Apache Cassandra from Windows CMD prompt
Start Apache Cassandra from Windows CMD prompt

Access Apache Cassandra cqlsh bash shell from Windows CMD

Enter the following command to access the Cassandra cqlsh bash shell

cqlsh
 Cassandra cqlsh bash shell

Create keyspace-name, table and index in Apache Cassandra Database

For running out Spring Boot microservice we need to create keyspace-name, table and index on table in Cassandra through cqlsh bash shell. Here, we are creating a keyspace named bootdb along with table customer having id as primary key.

======== cqlsh commands ===================================
cqlsh> create keyspace bootdb with replication={'class':'SimpleStrategy', 'replication_factor':1};
cqlsh> use bootdb;
cqlsh:bootdb> create table customer(id int primary key, firstanme text, lastname text, age int);
cqlsh:bootdb> create index on bootdb.customer(firstanme);
cqlsh:bootdb>

Build a Spring Boot Microservice with REST API

Technology Stack

Java 8
Maven 3.5.0
Spring Boot 2.4.2
Eclipse
Spring Boot Starter Data Cassandra
Spring Boot Rest API
Apache Cassandra 3.11.10

Application Project Structure

Application Project Structure

Maven File (pom.xml)

Project’s maven file pom.xml would be looked like as below.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.example</groupId>
	<artifactId>spring-boot-data-cassandra</artifactId>
	<version>0.0.1</version>
	<packaging>jar</packaging>

	<name>spring-boot-data-cassandra</name>
	<description>Spring Boot Data Cassandra </description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-cassandra</artifactId>			
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

Application Configurations: application.properties

spring.data.cassandra.keyspace-name=bootdb

spring.data.cassandra.schema-act=create_if_not_exists
spring.data.cassandra.contact-points=datacenter1

spring.data.cassandra.port=9042

#app config
server.port=8080
spring.application.name=SpringBootCassandra
server.servlet.context-path=/customer

Note: We need to configure contact-points as datacenter1 otherwise it will throw connection exception.

Spring Boot Application : @SpringBootApplication

package com.the.basic.tech.info.cassandra;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringDataCassandraApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringDataCassandraApplication.class, args);
	}
}

Spring Boot : Rest API @RestController (CustomerController.java)

In this java class we will expose REST APIs using @RestController annotations as below.

package com.the.basic.tech.info.cassandra.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.the.basic.tech.info.cassandra.model.Customer;
import com.the.basic.tech.info.cassandra.repository.CustomerRepository;

@RestController
@RequestMapping(value = "/")
public class CustomerController {

	@Autowired
	private CustomerRepository customerRepository;

	@RequestMapping("/save")
	public String save() {
		Customer cust_1 = new Customer(1, "Peter", "Smith", 20);
		Customer cust_2 = new Customer(2, "Mary", "Taylor", 25);
		Customer cust_3 = new Customer(3, "Peter", "Brown", 30);
		Customer cust_4 = new Customer(4, "Lauren", "Taylor", 20);
		Customer cust_5 = new Customer(5, "Lauren", "Flores", 45);
		Customer cust_6 = new Customer(6, "Peter", "Williams", 20);

		// save customers to ElasticSearch
		customerRepository.save(cust_1);
		customerRepository.save(cust_2);
		customerRepository.save(cust_3);
		customerRepository.save(cust_4);
		customerRepository.save(cust_5);
		customerRepository.save(cust_6);

		return "Save Operation Executed Successfully. Please call findall API.";
	}

	@RequestMapping("/findall")
	public String findAll() {
		String result = "";
		List<Customer> customers = customerRepository.findAll();
		if (customers.size() != 0) {
			for (Customer customer : customers) {
				result += customer.toString() + "<br>";
			}
		} else {
			result = "No Record Found!";
		}

		return result;
	}

	@RequestMapping("/findbyfirstname")
	public String findByFirstName(@RequestParam("firstname") String firstname) {
		List<Customer> customers = customerRepository.findByFirstname(firstname);
		String result = "";
		for (Customer customer : customers) {
			result += customer.toString() + "<br>";
		}
		return result;
	}

	@RequestMapping("/findbyagegreaterthan")
	public String findByAgeGreaterThan(@RequestParam("age") int age) {
		List<Customer> customers = customerRepository.findCustomerHasAgeGreaterThan(age);
		String result = "";
		for (Customer customer : customers) {
			result += customer.toString() + "<br>";
		}
		return result;
	}

	@RequestMapping("/findbyid")
	public String findById(@RequestParam("id") int id) {
		List<Customer> customers = customerRepository.findById(id);
		String result = "";
		for (Customer customer : customers) {
			result += customer.toString() + "<br>";
		}
		return result;
	}

	@RequestMapping("/deleteall")
	public String deleteAll() {
		customerRepository.deleteAll();

		return "Delete Operation Executed Successfully. Please call findall API.";
	}
}

Spring Boot : Add Customer Model Layer

We are adding Customer entity for mapping the attributes.

package com.the.basic.tech.info.cassandra.model;

import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.Table;

@Table
public class Customer {
	
	@PrimaryKey
	private int id;
	private String firstanme;
	private String lastname;
	private int age;
	
	public Customer(){}
	
	public Customer(int id, String firstname, String lastname, int age){
		this.id = id;
		this.firstanme = firstname;
		this.lastname = lastname;
		this.age = age;
	}
	
	public void setId(int id){
		this.id = id;
	}
	
	public int getId(){
		return this.id;
	}
	
	public void setFirstname(String firstname){
		this.firstanme = firstname;
	}
	
	public String getFirstname(){
		return this.firstanme;
	}
	
	public void setLastname(String lastname){
		this.lastname = lastname;
	}
	
	public String getLastname(){
		return this.lastname;
	}
	
	public void setAge(int age){
		this.age = age;
	}
	
	public int getAge(){
		return this.age;
	}
	
	@Override
	public String toString() {
		return String.format("Customer[id=%d, firstName='%s', lastName='%s', age=%d]", this.id, this.firstanme, this.lastname, this.age);
	}
}

Spring Boot : How to implement CustomerRepository

We need to extends CrudRepository Interface as shown below. Basically here we are writing our DB Queries.

package com.the.basic.tech.info.cassandra.repository;

import java.util.List;

import org.springframework.data.cassandra.repository.Query;
import org.springframework.data.repository.CrudRepository;

import com.the.basic.tech.info.cassandra.model.Customer;

public interface CustomerRepository extends CrudRepository<Customer, String> {
	
	@Query(value="SELECT * FROM customer WHERE firstanme=?0")
	public List<Customer> findByFirstname(String firstname);

	@Query("SELECT * FROM customer WHERE age > ?0 ALLOW FILTERING")
	public List<Customer> findCustomerHasAgeGreaterThan(int age);
	
	@Query("SELECT * FROM customer")
	public List<Customer> findAll();
	
	@Query("SELECT * FROM customer WHERE id =?0")
	public List<Customer> findById(int id);
}

Application (microservice) Deploy, Run & Test

Run spring-boot-data-cassandra: mvn spring-boot:run

D:\development\spring-boot-data-cassandra>mvn spring-boot:run
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building spring-boot-data-cassandra 0.0.1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.4.2:run (default-cli) > test-compile @ spring-boot-data-cassandra >>>
[INFO]
[INFO] --- maven-resources-plugin:3.2.0:resources (default-resources) @ spring-boot-data-cassandra ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] Copying 1 resource
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ spring-boot-data-cassandra ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:3.2.0:testResources (default-testResources) @ spring-boot-data-cassandra ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] skip non existing resourceDirectory D:\development\spring-boot-data-cassandra\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ spring-boot-data-cassandra ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] <<< spring-boot-maven-plugin:2.4.2:run (default-cli) < test-compile @ spring-boot-data-cassandra <<<
[INFO]
[INFO]
[INFO] --- spring-boot-maven-plugin:2.4.2:run (default-cli) @ spring-boot-data-cassandra ---
[INFO] Attaching agents: []

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.2)

2021-07-17 00:07:53.712  INFO 25584 --- [           main] t.b.t.i.c.SpringDataCassandraApplication : Starting SpringDataCassandraApplication using Java 1.8.0_212 on LOCALHOST with PID 25584 (D:\development\spring-boot-data-cassandra\target\classes started by 172025 in D:\development\spring-boot-data-cassandra)
2021-07-17 00:07:53.718  INFO 25584 --- [           main] t.b.t.i.c.SpringDataCassandraApplication : No active profile set, falling back to default profiles: default
2021-07-17 00:07:54.526  INFO 25584 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Reactive Cassandra repositories in DEFAULT mode.
2021-07-17 00:07:54.585  INFO 25584 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 46 ms. Found 0 Reactive Cassandra repository interfaces.
2021-07-17 00:07:54.593  INFO 25584 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Cassandra repositories in DEFAULT mode.
2021-07-17 00:07:54.625  INFO 25584 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 31 ms. Found 1 Cassandra repository interfaces.
2021-07-17 00:07:56.009  INFO 25584 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2021-07-17 00:07:56.026  INFO 25584 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-07-17 00:07:56.026  INFO 25584 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.41]
2021-07-17 00:07:56.232  INFO 25584 --- [           main] o.a.c.c.C.[.[localhost].[/customer]      : Initializing Spring embedded WebApplicationContext
2021-07-17 00:07:56.232  INFO 25584 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2439 ms
2021-07-17 00:08:03.355  INFO 25584 --- [           main] c.d.o.d.i.core.DefaultMavenCoordinates   : DataStax Java driver for Apache Cassandra(R) (com.datastax.oss:java-driver-core) version 4.9.0
2021-07-17 00:10:17.918  INFO 25584 --- [     s0-admin-0] c.d.oss.driver.internal.core.time.Clock  : Using native clock for microsecond precision
2021-07-17 00:10:17.922  INFO 25584 --- [     s0-admin-0] c.d.o.d.i.core.metadata.MetadataManager  : [s0] No contact points provided, defaulting to /127.0.0.1:9042
2021-07-17 00:10:20.728  INFO 25584 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2021-07-17 00:10:20.991  INFO 25584 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path '/customer'
2021-07-17 00:10:21.014  INFO 25584 --- [           main] t.b.t.i.c.SpringDataCassandraApplication : Started SpringDataCassandraApplication in 148.002 seconds (JVM running for 148.846)
2021-07-17 00:10:30.210  INFO 25584 --- [nio-8080-exec-1] o.a.c.c.C.[.[localhost].[/customer]      : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-07-17 00:10:30.212  INFO 25584 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2021-07-17 00:10:30.218  INFO 25584 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 2 ms

Spring Data Cassandra : fetch all the records from customer table (http://localhost:8080/customer/findall)

Spring Data Cassandra : fetch all the records from customer table

Spring Data Cassandra : create/insert records in customer table (http://localhost:8080/customer/save)

Spring Data Cassandra : fetch all the records from customer table (http://localhost:8080/customer/findall)

Spring Data Cassandra : fetch all the records from customer table

Spring Data Cassandra : fetch specific record from customer table having firstname ‘Mary’ (http://localhost:8080/customer/findbyfirstname?firstname=Mary)

Spring Data Cassandra : fetch specific record from customer table having firstname 'Mary'

Spring Data Cassandra : fetch specific record from customer table having age greater than ’20’ (http://localhost:8080/customer/findbyagegreaterthan?age=20)

Spring Data Cassandra : fetch specific record from customer table having age greater than '20'

Spring Data Cassandra : fetch specific record from customer table having id = ‘2’ (http://localhost:8080/customer/findbyid?id=2)

Spring Data Cassandra : fetch specific record from customer table having id = '2'

Spring Data Cassandra : delete/clear all records from customer table (http://localhost:8080/customer/deleteall)

Spring Data Cassandra : delete/clear all records from customer table

Cassandra Server Logs

We can see Truncate logs here.

INFO  [main] 2021-07-17 00:07:29,457 StorageService.java:1536 - JOINING: Finish joining ring
INFO  [main] 2021-07-17 00:07:29,521 SecondaryIndexManager.java:512 - Executing pre-join tasks for: CFS(Keyspace='bootdb', ColumnFamily='customer')
INFO  [main] 2021-07-17 00:07:29,601 StorageService.java:2452 - Node localhost/127.0.0.1 state jump to NORMAL
INFO  [main] 2021-07-17 00:07:30,121 NativeTransportService.java:73 - Netty using Java NIO event loop
INFO  [main] 2021-07-17 00:07:30,373 Server.java:158 - Using Netty Version: [netty-buffer=netty-buffer-4.0.44.Final.452812a, netty-codec=netty-codec-4.0.44.Final.452812a, netty-codec-haproxy=netty-codec-haproxy-4.0.44.Final.452812a, netty-codec-http=netty-codec-http-4.0.44.Final.452812a, netty-codec-socks=netty-codec-socks-4.0.44.Final.452812a, netty-common=netty-common-4.0.44.Final.452812a, netty-handler=netty-handler-4.0.44.Final.452812a, netty-tcnative=netty-tcnative-1.1.33.Fork26.142ecbb, netty-transport=netty-transport-4.0.44.Final.452812a, netty-transport-native-epoll=netty-transport-native-epoll-4.0.44.Final.452812a, netty-transport-rxtx=netty-transport-rxtx-4.0.44.Final.452812a, netty-transport-sctp=netty-transport-sctp-4.0.44.Final.452812a, netty-transport-udt=netty-transport-udt-4.0.44.Final.452812a]
INFO  [main] 2021-07-17 00:07:30,377 Server.java:159 - Starting listening for CQL clients on localhost/127.0.0.1:9042 (unencrypted)...
INFO  [main] 2021-07-17 00:07:31,516 CassandraDaemon.java:564 - Not starting RPC server as requested. Use JMX (StorageService->startRPCServer()) or nodetool (enablethrift) to start it
INFO  [main] 2021-07-17 00:07:31,517 CassandraDaemon.java:650 - Startup complete
INFO  [Native-Transport-Requests-1] 2021-07-17 00:12:45,963 OutboundTcpConnection.java:108 - OutboundTcpConnection using coalescing strategy DISABLED
INFO  [HANDSHAKE-localhost/127.0.0.1] 2021-07-17 00:12:46,020 OutboundTcpConnection.java:561 - Handshaking version with localhost/127.0.0.1
INFO  [MutationStage-2] 2021-07-17 00:12:46,041 ColumnFamilyStore.java:2221 - Truncating bootdb.customer
INFO  [MutationStage-2] 2021-07-17 00:12:46,352 ColumnFamilyStore.java:2270 - Truncate of bootdb.customer is complete
INFO  [MutationStage-2] 2021-07-17 00:32:03,826 ColumnFamilyStore.java:2221 - Truncating bootdb.customer
INFO  [MutationStage-2] 2021-07-17 00:32:04,230 ColumnFamilyStore.java:2270 - Truncate of bootdb.customer is complete

Download the Source Code (Attached)

Have a great Day :). Drop your question in the comment box if any.