Spring data with cassandra giving IllegalStateException - spring-boot

I'm completly new to cassandra, so my error might be obvious.
I'm trying to create an application with spring boot (version 2.3.0.M2) that contacts a cassandra (version 3.11.6) installed in localhost.
I've got an java.lang.IllegalStateException with the message:
Since you provided explicit contact points, the local DC must be explicitly set (see basic.load-balancing-policy.local-datacenter in the config, or set it programmatically with SessionBuilder.withLocalDatacenter). Current contact points are: Node(endPoint=localhost:9042, hostId=16a785a4-eaf3-4a4d-a216-5244d75206aa, hashCode=7b0b99d7)=datacenter1. Current DCs in this cluster are: datacenter1
My pom is the following one:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.M2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.test</groupId>
<artifactId>cassandra</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cassandra</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>
</project>
In the application code, I've got a the following configuration class:
public class CassandraConfig extends AbstractCassandraConfiguration {
public static final String KEYSPACE = "test_keyspace";
#Override
public SchemaAction getSchemaAction() {
return SchemaAction.CREATE_IF_NOT_EXISTS;
}
#Override
protected List<CreateKeyspaceSpecification> getKeyspaceCreations() {
CreateKeyspaceSpecification specification = CreateKeyspaceSpecification.createKeyspace(KEYSPACE);
return Arrays.asList(specification);
}
#Override
protected List<DropKeyspaceSpecification> getKeyspaceDrops() {
return Arrays.asList(DropKeyspaceSpecification.dropKeyspace(KEYSPACE));
}
#Override
protected String getKeyspaceName() {
return KEYSPACE;
}
#Override
public String[] getEntityBasePackages() {
return new String[]{"com.test.cassandra.entity"};
}
}
My properties file contains the following configuration:
spring.data.cassandara.keyspace-name=test_keyspace
spring.data.cassandra.contact-points=localhost
spring.data.cassanda.port=9042
spring.data.cassandra.schema-act=create_if_not_exists
I also have tried with
spring.data.cassandra.contact-points=dc1
When I execute the application, I got see from the logs that I'm using the following version
DataStax Java driver for Apache Cassandra(R) (com.datastax.oss:java-driver-core) version 4.4.0
In the cassandra-rackdc.properties I've set the name to
dc=dc1
I've done several test adding configuration parameters, even adding an application.conf to the classpath as described datastax documentation, but didn't have any success. Any clue where should I do it?

Simply add
spring.data.cassandra.local-datacenter=DC1
to your application.properties file, where DC1 your local datacenter name (default DC1).

Since Spring Boot 2.3.0.X you need to explicitly provide the name of the local datacenter (because of the added Cassandra driver 4.X support which requires local datacenter to be set explicitly). You can do this by overrirding the getLocalDataCenter method in AbstractSessionConfiguration. AbstractCassandraConfiguration extends this class so you just need to add in your CassandraConfig class:
#Override
protected String getLocalDataCenter() {
return "dc1";
}

Run nodetool status in the Cassandra shell. It gives you the datacenter name.
> root#d21e7b0dfc44:/# nodetool status
Datacenter: datacenter1
=======================
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
-- Address Load Tokens Owns (effective) Host ID Rack
UN 172.17.0.2 143.37 KiB 16 ? a3584baf-0665-474b-84e9-ba30528a7781 rack1
root#d21e7b0dfc44:/#
In my case, it is "datacenter1". This goes inside the properties file like so:
spring.data.cassandra.local-datacenter=datacenter1

There's actually a different config needed for Apache Cassandra to truly work with the new spring versions. It took me about 50 hours of searching before finding this out on this documentation - https://docs.spring.io/spring-data/cassandra/docs/current/reference/html/#cassandra.cassandra-java-config
You'll need to create a new config file as specified there. To do this, follow thus:
Create a new package named config
Create a .java class file in the package named - CassandraConfig
Copy and paste the below code into it.
#Configuration public class CassandraConfig { public #Bean CqlSession session() { return CqlSession.builder().withKeyspace("mykeyspacename").build(); } }
Note, change the Keyspace name to your Keyspace name defined in your property file. If you have not created a Keyspace, start Cassandra server on your system, login to the cqlsh on the terminal or cmd by issuing cqlsh, and issue this command -
CREATE KEYSPACE IF NOT EXISTS mykeyspacename WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 } AND DURABLE_WRITES = true ;
By now, when you start the spring boot app, it should run without any error.

Since Spring Boot 3.0 Cassandra properties' prefix is spring.cassandra.*, not spring.data.cassandra.* (specifically since 3.0.0-M5, as can be seen from #ConfigurationProperties.prefix value here and here),
so you need to write
spring.cassandra.local-datacenter=datacenter1
in your application.properties file to set local datacenter

Its mentioned in your error logs "Current DCs in this cluster are: datacenter1"
Just add spring.data.cassandra.local-datacenter=datacenter1 in application.properties

I solved this problem by setting local datacenter like this :
application properties file : cassandra.local.datacenter=datacenter1
#Value("${cassandra.local.datacenter}")
private String localDatacenter;
#Bean
#Override
public CqlSessionFactoryBean cassandraSession() {
CqlSessionFactoryBean cqlSessionFactoryBean = new CqlSessionFactoryBean();
cqlSessionFactoryBean.setUsername(username);
cqlSessionFactoryBean.setPassword(password);
cqlSessionFactoryBean.setKeyspaceName(keySpace);
cqlSessionFactoryBean.setLocalDatacenter(localDatacenter);
cqlSessionFactoryBean.setPort(port);
cqlSessionFactoryBean.setContactPoints(contactPoint);
return cqlSessionFactoryBean;
}
Note : This is not all of the configuration code. I just wanted to show local datacenter configuration by this code snippet.

Related

Spring Boot Spark on K8S (Minikube): cannot assign instance of java.lang.invoke.SerializedLambda

I've seen others have been dealing with this same issue, but since none of the proposed solutions or workarounds worked for me and I've already spent hours on this, I figured I would share my specific case in detail in hope someone could point out what I'm missing.
I wanted to experiment with running a very simple Spark Spring-Boot application on a Minikube k8s cluster. When I run the app locally (using SparkSession.builder().master("local")) everything works as expected. However, when I deploy my app to minikube, I manage to get my driver pod to spin up the executor pods when the job is triggered, but then I get this exception on my executor pods:
ERROR Executor: Exception in task 0.1 in stage 0.0 (TID 1)
cannot assign instance of java.lang.invoke.SerializedLambda to field org.apache.spark.sql.execution.MapPartitionsExec.func of type scala.Function1 in instance of org.apache.spark.sql.execution.MapPartitionsExec
Here is my spring-boot app. For the sake of simplicity of sharing this, I kept all the logic on the controller:
WordcountController
#RestController
public class WordCountController implements Serializable {
#PostMapping("/wordcount")
public ResponseEntity<String> handleFileUpload(#RequestParam("file") MultipartFile file) throws IOException {
String hostIp;
try {
hostIp = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
SparkConf conf = new SparkConf();
conf.setAppName("count.words.in.file")
.setMaster("k8s://https://kubernetes.default.svc:443")
.setJars(new String[]{"/app/wordcount.jar"})
.set("spark.driver.host", hostIp)
.set("spark.driver.port", "8080")
.set("spark.kubernetes.namespace", "default")
.set("spark.kubernetes.container.image", "spark:3.3.2h.1")
.set("spark.executor.cores", "2")
.set("spark.executor.memory", "1g")
.set("spark.kubernetes.authenticate.executor.serviceAccountName", "spark")
.set("spark.kubernetes.dynamicAllocation.deleteGracePeriod", "20")
.set("spark.cores.max", "4")
.set("spark.executor.instances", "2");
SparkSession spark = SparkSession.builder()
.config(conf)
.getOrCreate();
byte[] byteArray = file.getBytes();
String contents = new String(byteArray, StandardCharsets.UTF_8);
Dataset<String> text = spark.createDataset(Arrays.asList(contents), Encoders.STRING());
Dataset<String> wordsDataset = text.flatMap((FlatMapFunction<String, String>) line -> {
List<String> words = new ArrayList<>();
for (String word : line.split(" ")) {
words.add(word);
}
return words.iterator();
}, Encoders.STRING());
// Count the number of occurrences of each word
Dataset<Row> wordCounts = wordsDataset.groupBy("value")
.agg(count("*").as("count"))
.orderBy(desc("count"));
// Convert the word count results to a List of Rows
List<Row> wordCountsList = wordCounts.collectAsList();
StringBuilder resultStringBuffer = new StringBuilder();
// Build the final string representation
for (Row row : wordCountsList) {
resultStringBuffer.append(row.getString(0)).append(": ").append(row.getLong(1)).append("\n");
}
return ResponseEntity.ok(resultStringBuffer.toString());
}
Here is my maven pom.xml:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.8</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>wordcount</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>wordcount</name>
<description>wordcount</description>
<properties>
<java.version>11</java.version>
<spark.version>3.3.2</spark.version>
<scala.version>2.12</scala.version>
</properties>
<dependencyManagement>
<dependencies>
<!--Spark java.lang.NoClassDefFoundError: org/codehaus/janino/InternalCompilerException-->
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>commons-compiler</artifactId>
<version>3.0.8</version>
</dependency>
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>3.0.8</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>commons-compiler</artifactId>
<version>3.0.8</version>
</dependency>
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>3.0.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_${scala.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency> <!-- Spark dependency -->
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_${scala.version}</artifactId>
<version>${spark.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-kubernetes_${scala.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
and here is the Dockerfile I'm using to package my spring-boot application before deploying it to minikube:
# Use an existing image as the base image
FROM openjdk:11-jdk
# Set the working directory
WORKDIR /app
# Copy the compiled JAR file to the image
COPY target/wordcount-0.0.1-SNAPSHOT.jar /app/wordcount.jar
RUN useradd -u 185 sparkuser
# Set the entrypoint command to run the JAR file
ENTRYPOINT ["java", "-jar", "wordcount.jar"]
For the spark.kubernetes.container.image I built a docker image using the Dockerfile which is shipped with my local Spark bin (spark-3.3.2-bin-hadoop3 - same Spark version used by my spring-boot app) following these instructions and loaded it to minikube.
Here are some of the things I tried with no luck so far:
Share my app's jar with Spark using setJars(new String[]{"/app/wordcount.jar"}) as suggested here - this absolute file-path is where my app's jar lives on my driver image
use maven-shade-plugin as suggested here to change the way my app's jar distributes its dependencies - this resulted in a ClassNotFoundException: SparkSession exception on my driver pod.
Refactor my controller's code to not use lambda functions (didn't make a difference):
public static class SplitLine implements FlatMapFunction<String, String> {
#Override
public Iterator<String> call(String line) throws Exception {
List<String> words = new ArrayList<>();
for (String word : line.split(" ")) {
words.add(word);
}
return words.iterator();
}
...
Dataset<String> wordsDataset = text.flatMap(new SplitLine(), Encoders.STRING());
Any tips or hints regarding my setup or suggestions on how I can refactor my code to get it to work with the existing setup would be greatly appreciated.

error while reloading spring properties with #RefreshScope in spring cloud

I'm testing one prototype for spring-cloud using #RefreshScope annotation. I'm using one external property file for that. Which I have provided and configured in runtime argument.
pom.xml dependency
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.proto.reload</groupId>
<artifactId>CachingService</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>CachingService</name>
<description>Demo project for cache</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<version>1.1.8.RELEASE</version>
</dependency>
<!-- <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-client</artifactId>
<version>1.1.3.RELEASE</version> </dependency> -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
This is main class
package com.proto.reload;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.ApplicationPidFileWriter;
#SpringBootApplication
public class CachingServiceApplication {
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Invalid Commandline argument. Please provide Consumer Properties file.");
System.exit(0);
}
new SpringApplicationBuilder(CachingServiceApplication.class)
.listeners(new ApplicationPidFileWriter())
.properties("spring.config.name:" + args[0].replaceAll(".properties", ""))
.build()
.run(args);
while (true) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ConfigClass.getName());
}
}
}
Config class having #RefreshScope annotation
package com.proto.reload;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
#Configuration
#RefreshScope
public class ConfigClass {
private static String name;
public static String getName() {
return name;
}
#Value("${app.prop.name}")
public void setName(String name) {
this.name = name;
}
}
And this is property file I'm passing through argument
app.prop.name=FirstOldValue
spring.pid.file=TestOne.pid
When I run this code, I get the following error.
12:38:41.051 [main] DEBUG org.springframework.boot.context.logging.ClasspathLoggingApplicationListener - Application failed to start with classpath: [file:/C:/Program%20Files/Java/jre1.8.0_191/lib/resources.jar, file:/C:/Program%20Files/Java/jre1.8.0_191/lib/rt.jar, file:/C:/Program%20Files/Java/jre1.8.0_191/lib/jsse.jar, file:/C:/Program%20Files/Java/jre1.8.0_191/lib/jce.jar, file:/C:/Program%20Files/Java/jre1.8.0_191/lib/charsets.jar, file:/C:/Program%20Files/Java/jre1.8.0_191/lib/jfr.jar, file:/C:/Program%20Files/Java/jre1.8.0_191/lib/ext/access-bridge-64.jar, file:/C:/Program%20Files/Java/jre1.8.0_191/lib/ext/cldrdata.jar, ...]
12:38:41.105 [main] ERROR org.springframework.boot.SpringApplication - Application run failed
java.lang.NoSuchMethodError: org.springframework.boot.builder.SpringApplicationBuilder.<init>([Ljava/lang/Object;)V
at org.springframework.cloud.bootstrap.BootstrapApplicationListener.bootstrapServiceContext(BootstrapApplicationListener.java:120)
at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:84)
at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:62)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:75)
at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:54)
at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:347)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:306)
at com.proto.reload.CachingServiceApplication.main(CachingServiceApplication.java:19)
Am I using #RefreshScope in wrong way ?
This code works fine when I remove dependency and #RefreshScope, but its not updating property value at runtime. My goal is to load modified property value at runtime.
1.1.8.RELEASE for Spring Cloud is part of the Camden Release train which is for Spring Boot 1.4.x releases.
Please see the Release Trains section for what release train to use for your Spring Boot version
https://spring.io/projects/spring-cloud
Currently they are,
Release Train Boot Version
Greenwich 2.1.x
Finchley 2.0.x
Edgware 1.5.x
Dalston 1.5.x
As your using spring boot 2.1.1 your dependency management should look like the below, note as Greenwich doesn't have a RELEASE yet you need to add the milestone repository.
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RC2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</dependency>
</dependencies>
try this
#RefreshScope
public class ConfigClass {
#Value("${app.prop.name}")
private static String name;
public static String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Add cloud context depedency and try again
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<version>1.1.8.RELEASE</version>
</dependency>
Update :
it is just not a valid use case to have #Configuration and #RefreshScope on the same element. As per this URL thread : https://github.com/spring-cloud/spring-cloud-config/issues/43

Spring Boot project shows the Login page

I created a Spring boot project with the Spring initializer and only have the starter code so far.
The sample code
#SpringBootApplication
public class EcommerceApplication {
public static void main(String[] args) {
SpringApplication.run(EcommerceApplication.class, args);
}
}
#Controller
#RequestMapping(value = "/")
public class HomeController {
#GetMapping
public String index() {
return "index";
}
}
I expect it to return the index.html page from the templates folder. Instead, I'm redirected to the http://localhost:8080/login address and asked for the username/ password. In the application.properties file, I tried to use the configuration of,
server.port=8080
spring.application.name=Bootstrap Spring Boot
spring.thymeleaf.cache=false
spring.thymeleaf.enabled=true
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:bootapp;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username=testuser
spring.datasource.password=testpassword
server.error.path=/error
server.error.whitelabel.enabled=false
I tried to login with the username testuser and the password testpassword. This doesn't work out.
I use the following pom.xml in the project,
<?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.online.books</groupId>
<artifactId>Ecommerce</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Ecommerce</name>
<description>Spring Boot Project</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</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-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!--<dependency>-->
<!--<groupId>com.h2database</groupId>-->
<!--<artifactId>h2</artifactId>-->
<!--<scope>runtime</scope>-->
<!--</dependency>-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
Whats the issue here?
If you don't want login page (from Spring-Security) remove the following dependency from your pom.xml and do maven update to refresh the dependencies on the class path.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Or if you want to use the Spring-Security then on console it will display the default password like below :
Using default security password: ce6c3d39-8f20-4a41-8e01-803166bb99b6
the default username will be user
That is the default behavior. to change this, you have a few options:
You can remove the Spring Boot Security dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
You can desable autoconfiguration. To do so; in your main class, to: #SpringBootApplication append: (exclude = { SecurityAutoConfiguration.class }) so that it looks like:
#SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
public static void main(String[] args) {
SpringApplication.run(SpringBootSecurityApplication.class, args);
}
}
you can also do this from the application.properties file
For more information on desableing Auto-Configuration and setting up your own. Reference:
Spring Boot Security Auto-Configuration
When you include spring-boot-starter-security then the login page will automatically be shown.
To remove this login page-
If you use Maven then removing this dependency and rebuild the project. Already there are some answers for this. Just remove the block (pom.xml):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
If anyone uses Gradle then, just remove the following block from dependencies block(build.gradle) and reload build.gradle:
dependencies {
//implementation 'org.springframework.boot:spring-boot-starter-security'
...
}
If you do not want to change the config file like pom.xml or build.gradle then source code level change will be the best suit for you. To do this, need to update the main class with, change #SpringBootApplication annotation with #SpringBootApplication(exclude = {SecurityAutoConfiguration.class}). This excludes parameter will remove Security configuration.
#SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class SpringCacheApplication {
public static void main(String[] args) {
...
}
...
}
Moreover, if you want to keep the login page, then you can log in with generated security password. You can find it into console, looks like:
Using generated security password: d408ce6f-470d-4**4-950a-81**9651f321
{
"usename" : "user",
"password": "d408ce6f-470d-4**4-950a-81**9651f32"
}
For more information, you can read Spring Security.
You can add a class in your Project,
for Web Security configuration as follows:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/*").permitAll()
.and()
.csrf().disable();
}
}
Check the console to find the password
Also as a username please use "user" as a credential.
If you want to disable that spring functionality, go to your pom xml and delete the dependency below;
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
if you really want to disable this login page you may disable your class where is:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
this class is not necessary
in my opinion you haven't create this class whose extends WebSecurityConfigurerAdapter
My code without the starter-security but still ask for login.
Here is the solution:
disable your autoconfiguration in main class,
#SpringBootApplication(exclude= {SecurityAutoConfiguration.class})
If you want the spring security and you've defined the WebSecurityConfig then make sure you have these two annotations on your WebSecurityConfig class:
#Configuration
#EnableWebSecurity
if you have login page displayed; it means that security is activated;
you comment or delete those dependencies from pom.xml:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
or:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
or:
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
I had the same issue and in my case it was because of missing #Configuration and #EnableWebSecurity annotations in SecurityConfig file

Not able to run the starter guide code of Building Spring Boot Rest Service

I am trying to build rest service using Spring Boot. I am learning to implement it using the this Spring's guide. I have built the application using STS as a Maven application and the code is exact replica of the guide and when I try to run the application I get the below connection error
I also tried to run the github code for the same guide provided by Spring and that code also gives same the error as above. I am new to Spring Boot and any help would be appreciated.
Below is my code
App.java
package Greetings.Web_Services;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class App
{
public static void main( String[] args )
{
System.out.println("App Started");
SpringApplication.run(App.class, args);
}
}
Greetings.java
package Greetings.Web_Services;
public class Greetings {
private final long id;
private final String content;
public Greetings(long id, String content){
this.id = id;
this.content = content;
}
public long getId(){
return id;
}
public String getContent(){
return content;
}
}
GreetingsController.java
package Greetings.Web_Services;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class GreetingsController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#RequestMapping("/greeting")
public Greetings greeting(#RequestParam(value="name", defaultValue="World") String name) {
return new Greetings(counter.incrementAndGet(),
String.format(template, name));
}
}
pom.xml
<?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>org.springframework</groupId>
<artifactId>gs-rest-service</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency> -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
</project>
GitHub link for the project of above code
Thanks!!
The code in the guide is correct and it was not working earlier for which I had also raised an issue in the GitHub community of the guide. Later when I updated the STS and restarted, it seems to work now. One of the modifier in the community also recommended the same after I explained the scenario. Hope this saves someone's time.

Configure integration test or use Spring JDBC in Unit Test using Spring Boot?

Am using the latest versions of Spring Boot, Spring JDBC, and Spring Rest...
My project is setup as a typical Maven project containing the following filesystem structure:
myproject
|
--src/main/java/com/myapp
--src/main/resource/application.properties
|
--src/test/java/com/myapp
--src/test/resources/application.properties
|
pom.xml
My application.properties are as follows (connecting to a local MySQL 5 database):
spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=root
spring.datasource.password=
spring.datasource.name=testdb
spring.datasource.initialize=true
spring.datasource.driverClassName=com.mysql.jdbc.Driver
MyDAO:
public interface MyDao {
public List<App> findAllApps();
}
MyDaoImpl:
#Repository("myDao")
public class MyDaoImpl implements MyDao {
#Autowired
JdbcTemplate jdbcTemplate;
public List<App> findAllApps() {
List<App> apps = this.jdbcTemplate.query(
"select app_name from app",
new RowMapper<App>() {
public App mapRow(ResultSet rs, int rowNum) throws SQLException {
App app = new App();
app.setAppName(rs.getString("app_name"));
return app;
}
});
return apps;
}
}
Its called in MyService class using Dependency Injection:
#RestController
public class MyService {
#Autowired
#Qualifier("myDao")
MyDao myDao;
#RequestMapping(value = "/apps", method = RequestMethod.GET, consumes = "text/plain", produces = "application/json")
public void process() throws JsonParseException, IOException {
List<App> apps = myDao.findAllApps();
System.out.println(apps.toString());
}
}
This totally works as stated in my RestController...
But however, in a typical JUnit test:
public class MyServiceTest {
#Autowired
#Qualifier("myDao")
MyDao myDao;
#Test
public void process() {
List<App> apps = myDao.findAllApps();
}
}
The call to myDao.findAllApps() returns a NullPointerException...
I even tried running my app (using the embedded tomcat) first by issuing the following from the command line:
mvn spring-boot:run
However a non-database specific JUnit test works inside Eclipse or when I do:
mvn clean install
Question(s):
How can I set it up so I can run an integration test and it actually hits my database (or a mock db for that matter) from MyServiceTest?
Why is the dependency injection failing when trying to inject in MyServiceTest for Spring JDBC?
Is there a way to setup my unit tests to test Rest calls?
Many thanks for everyone who took the time to read this and many many thanks for the people that respond!
Here's my pom.xml (per Eddu's request):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>myproject</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.2.RELEASE</version>
</parent>
<properties>
<java.version>1.7</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</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-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/libs-release</url>
</repository>
<repository>
<id>org.jboss.repository.releases</id>
<name>JBoss Maven Release Repository</name>
<url>https://repository.jboss.org/nexus/content/repositories/releases</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
</project>
It seems you aren't loading Spring's context in your test so the dependency injection isn't performed. You should do something like:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=Application.class) //Application being your
// Spring boot base config class
public class MyServiceTest { ... }
#Autowired
#Qualifier("myDao")
MyDao myDao;
You can have a look at an example in my github
Thanks everyone!
Steve - you were correct!
After much trial & tribulation, the #SpringApplicationConfiguration worked:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes=Application.class)
Eddu Melendez - I removed the spring-starter-data-jpa dependency with spring-boot-starter-jdbc and also removed the spring-jdbc dependency from my pom.xml file.
Thank you all for trying to help me solve this issue!

Resources