I am trying to experiment by building some simple API's with apache camel. The request body for a post request is empty but camel doesn't return bad request instead it returns 201.
Here is what I do;
Generate classes from studentreg-api.yaml [This is a valid
openapi 3 yaml document]
Using camel, spring boot to build
REST endpoints Step
Configured camel rest endpoint with clientRequestValidation(true)
Start the server
Open /api-docs and validated if the mandatory attributes are marked
appropriately
From postman made request to the POST endpoint with blank request body
Expected: 400 - Bad request
Actual: 201
I couldn't figure out what is that I am missing.
BaseRouteConfig.java
#ComponentScan
#Configuration
public class BaseRouteConfig extends RouteBuilder {
#Autowired
private Environment environment;
#Value("${studentregistration.api.path}")
private String basePath;
#Bean
ServletRegistrationBean servletRegistrationBean() {
ServletRegistrationBean servlet = new ServletRegistrationBean(new CamelHttpTransportServlet(), basePath + "/*");
servlet.setName("CamelServlet");
return servlet;
}
#Override
public void configure() throws Exception {
restConfiguration().component("servlet")
.bindingMode(RestBindingMode.json)
.dataFormatProperty("pretty-print", "true")
.enableCORS(true)
.port(environment.getProperty("server.port", "8085"))
.contextPath(basePath)
.apiContextPath("/api-docs")
.apiVendorExtension(true)
.apiProperty("api.title", "Student registration")
.apiProperty("api.version", "0.1")
.apiProperty("cors", "true")
.clientRequestValidation(true);
}
}
RegisterStudentRoute.java
#Component
public class RegisterStudentRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
rest("/api/").consumes(MediaType.APPLICATION_JSON_VALUE)
.produces(MediaType.APPLICATION_JSON_VALUE)
.post("/registration")
.type(RegisterStudentRequest.class)
.clientRequestValidation(true)
.outType(RegisterStudentResponse.class)
.responseMessage().code(HttpStatus.CREATED.value())
.endResponseMessage()
.route().routeId("create-student-registration")
.setHeader(Exchange.HTTP_RESPONSE_CODE, simple("201"))
.to("log:success")
.end().endRest();
}
}
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.5.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>camel-rest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>camel-rest</name>
<description>Demo project for Spring Boot & camel rest</description>
<properties>
<java.version>16</java.version>
<camel.version>3.10.0</camel.version>
<openapi-generator.version>5.1.0</openapi-generator.version>
<springfox.version>2.9.2</springfox.version>
<jackson-databind.version>0.2.1</jackson-databind.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-servlet-starter</artifactId>
<version>3.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-jackson-starter</artifactId>
<version>3.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-openapi-java-starter</artifactId>
<version>3.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>3.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-http-starter</artifactId>
<version>3.9.0</version>
</dependency>
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator</artifactId>
<version>${openapi-generator.version}</version>
</dependency>
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>jackson-databind-nullable</artifactId>
<version>${jackson-databind.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${springfox.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<!-- RELEASE_VERSION -->
<version>5.1.0</version>
<!-- /RELEASE_VERSION -->
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/api.yaml</inputSpec>
<generatorName>spring</generatorName>
<apiPackage>com.sample.camel-rest.api</apiPackage>
<modelPackage>com.sample.camel-rest.model</modelPackage>
<supportingFilesToGenerate>
ApiUtil.java
</supportingFilesToGenerate>
<configOptions>
<delegatePattern>true</delegatePattern>
<outputDir>${project.basedir}/generated</outputDir>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
ok! I have figured out a simpler way to handle validation. Adding a route to bean-validator solved the problem! If we need to build custom error messages or if you need to build error model based on failures, you can use camel processor and javax.validator.
RegisterStudentRoute.java
#Component
public class RegisterStudentRoute extends RouteBuilder {
#Autowired
RegisterStudentRequestValidator registerStudentRequestValidator;
#Override
public void configure() throws Exception {
rest("/api/").consumes(MediaType.APPLICATION_JSON_VALUE)
.produces(MediaType.APPLICATION_JSON_VALUE)
.post("/registration")
.type(RegisterStudentRequest.class)
.clientRequestValidation(true)
.param().name("request-id").dataType("string")
.type(RestParamType.header).required(true)
.endParam()
.outType(RegisterStudentResponse.class)
.responseMessage().code(HttpStatus.CREATED.value())
.endResponseMessage()
.route().routeId("create-student-registration")
// .process(registerStudentRequestValidator)
.to("direct:validateReq")
.setHeader(Exchange.HTTP_RESPONSE_CODE, simple("201"))
.to("log:success")
.end().endRest();
from("direct:validateReq")
.to("bean-validator://x")
.to("mock:end");
}
Related
In sandbox project trying reactive webflux with springboot and postgres. I added r2dbc, created endpoints - get all records and post endpoints work, but appeared problem with requests which use path variable (get one record, or delete by id)
Here is my code:
#SpringBootApplication
#EnableR2dbcAuditing
public class Springboot2Application {
public static void main(String[] args) {
SpringApplication.run(Springboot2Application.class, args);
}
}
Controller:
#RequiredArgsConstructor
#RestController
#RequestMapping("/api/v1")
public class ToDoController {
private final ToDoRepository repository;
#GetMapping(value = "/to-do/{toDoId}", produces = {
MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE})
public Mono<ResponseEntity<ToDo>> getToDo(#Valid #PathVariable Long toDoId) {
return repository.findById(toDoId)
.map(ResponseEntity::ok);
}
}
repository:
#Repository
public interface ToDoRepository extends R2dbcRepository<ToDo,Long> {
}
entity:
#Data
#RequiredArgsConstructor
#Table(name = "to_do")
public class ToDo {
#Id
private Long id;
#Version
private Long version;
#NotNull
#NotBlank
private String description;
#CreatedDate
private Timestamp created;
#LastModifiedDate
private Timestamp modified;
private boolean completed;
}
r2dbc config:
#Configuration
#EnableR2dbcRepositories(basePackages = "com.springboot2.repository")
public class R2DBCConfig extends AbstractR2dbcConfiguration {
#Bean
public ConnectionFactory connectionFactory() {
return ConnectionFactories.get(
ConnectionFactoryOptions.builder()
.option(DRIVER, "postgresql")
.option(HOST, "localhost")
.option(PORT, 5432)
.option(USER, "admin")
.option(PASSWORD, "admin")
.option(DATABASE, "springdb")
.build());
}
#Bean
ReactiveTransactionManager transactionManager(ConnectionFactory connectionFactory) {
return new R2dbcTransactionManager(connectionFactory);
}
}
And my pom file:
<?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.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.springboot</groupId>
<artifactId>springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot2</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>8</java.version>
<spring-cloud.version>2021.0.3</spring-cloud.version>
</properties>
<dependencies>
<!-- DB,ORM, and plugins-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<version>0.8.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- Reactive libs-->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
table creation sql:
CREATE TABLE to_do
(
ID SERIAL primary key ,
version bigint,
description char(255),
created timestamp,
modified timestamp,
completed boolean
);
So when I execute GET http://localhost:8080/api/v1/to-do/3 I get:
java.lang.IllegalStateException: Required identifier property not found for class com.springboot2.domain.ToDo
I tried to define this using #Query in repository, but got same result. I suppose problem can be either in my table, or in entity, but I can't see it. And also seems #Version does not work(every time writes null to table)
The problem may be in wrong import of #Id annotation in your domain.
It should be from package
org.springframework.data.annotation.Id
But in your case it is from package
javax.persistence
Also, consider the naming of fields. As in your domain id is declared as "id", I can see in your create script id as "ID" (in uppercase).
I have created a Kinesis Analytics Streaming Application in SpringBoot which will consume messages from the AmazonKinesis input stream and will do some operations on top of it using the Apache Flink DataStream library.
When, I am uploading the application jar to S3 and trying to run this application on Streaming App it is throwing ClassNotFoundException for one of the files which is the ApplicationConfiguration file. Also, when I am running this application locally, it is running fine without any errors and I am able to consume the messages.
Below are some of the code files.
ApplicationConfiguration.java
import java.util.Properties;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.connectors.kinesis.FlinkKinesisConsumer;
import org.apache.flink.streaming.connectors.kinesis.FlinkKinesisProducer;
import org.apache.flink.streaming.connectors.kinesis.config.ConsumerConfigConstants;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class ApplicationConfiguration {
#Value("${oe.metric.input.stream}")
private String inputStreamName;
#Value("${oe.metric.output.stream}")
private String outputStreamName;
#Value("${aws.region}")
private String region;
#Value("${stream.initial.position}")
private String position;
#Bean
public FlinkKinesisProducer<String> createSinkFromStaticConfig() {
Properties outputProperties = new Properties();
outputProperties.setProperty(ConsumerConfigConstants.AWS_REGION, region);
outputProperties.setProperty("AggregationEnabled", "false");
FlinkKinesisProducer<String> sink = new FlinkKinesisProducer<>(new SimpleStringSchema(),
outputProperties);
sink.setDefaultStream(outputStreamName);
sink.setDefaultPartition("0");
return sink;
}
#Bean
public DataStream<String> createSourceFromConfig() {
Properties inputProperties = new Properties();
inputProperties.setProperty(ConsumerConfigConstants.AWS_REGION, region);
inputProperties.setProperty(ConsumerConfigConstants.STREAM_INITIAL_POSITION, position);
return ApplicationConstants.streamExecutionEnvironment.addSource(
new FlinkKinesisConsumer<>(inputStreamName, new SimpleStringSchema(), inputProperties));
}
Error:
Caused by: java.lang.IllegalStateException: Cannot load configuration class: com.pkg.config.ApplicationConfiguration
at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:415)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory(ConfigurationClassPostProcessor.java:268)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:325)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:147)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295)
at com.pkg.KinesisConsumerApplication.main(KinesisConsumerApplication.java:18)
... 25 more
Caused by: java.lang.ClassNotFoundException: com.pkg.config.ApplicationConfiguration
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:476)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:151)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:398)
KinesisConsumerApplication.java
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan(basePackages = "com.pkg")
public class KinesisConsumerApplication implements CommandLineRunner {
#Autowired
ApplicationContext ctx;
public static void main(String[] args) {
SpringApplication.run(KinesisConsumerApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
GetKinesisRecords getKinesisRecords = ctx.getBean(GetKinesisRecords.class);
getKinesisRecords.getDataFromStream();
}
}
Maven pom.xml file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<artifactId>kinesis-consumer</artifactId>
<build>
<plugins>
<plugin>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<artifactId>lombok</artifactId>
<groupId>org.projectlombok</groupId>
</exclude>
</excludes>
</configuration>
<groupId>org.springframework.boot</groupId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<artifactId>spring-boot-starter</artifactId>
<groupId>org.springframework.boot</groupId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<artifactId>flink-connector-kinesis_${scala.binary.version}</artifactId>
<groupId>org.apache.flink</groupId>
<version>1.13.2</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java_${scala.binary.version}</artifactId>
<version>1.13.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<artifactId>flink-core</artifactId>
<groupId>org.apache.flink</groupId>
<version>1.13.2</version>
</dependency>
<dependency>
<artifactId>jackson-datatype-jsr310</artifactId>
<groupId>com.fasterxml.jackson.datatype</groupId>
</dependency>
<dependency>
<artifactId>spring-boot-starter-test</artifactId>
<groupId>org.springframework.boot</groupId>
<scope>test</scope>
</dependency>
<dependency>
<artifactId>junit</artifactId>
<groupId>junit</groupId>
<scope>test</scope>
</dependency>
<dependency>
<artifactId>ssai-common</artifactId>
<groupId>com.adsparx.phoenix</groupId>
<version>1.2.0</version>
</dependency>
<dependency>
<artifactId>flink-clients</artifactId>
<groupId>org.apache.flink</groupId>
<version>1.15.0</version>
</dependency>
<dependency>
<artifactId>jakarta.xml.bind-api</artifactId>
<groupId>jakarta.xml.bind</groupId>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
</dependency>
</dependencies>
<description>Apache Kinesis Consumer</description>
<groupId>com.pkg</groupId>
<modelVersion>4.0.0</modelVersion>
<name>kinesis-consumer</name>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<relativePath/>
<version>2.4.0</version>
</parent>
<properties>
<java.version>11</java.version>
<scala.binary.version>2.12</scala.binary.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<version>0.0.1-SNAPSHOT</version>
</project>
Things that I have tried on my end:
Clean build the project a couple of times.
Checked Java and Spring version in pom, as suggested in one of the
StackOverflow post.
Downgraded SpringBoot version from "2.7.0" to "2.4.0".
Added ComponentScan annotation in main class.
Tried changing the annotation from #Configuration to #Component.
Last but not least, tried google and stackoverflow.
Can someone please help me identify the error?
Is this because you're attempting to use dependencies for flink 1.15? AFAIK, 1.13 is the latest version supported. You need to choose which flink version you intend to use when creating the streaming application.
I'm using Spring Boot v2.6.2 and Java v17 and trying to test my MongoConnection without having a MongoDBService running because it should be tested with embedded in-memory MongoDB on the build machine, no need to set up an extra MongoDB service there.
For sure on the productive system, it should use a full MongoDB.
I try to get this easy example running:
#DataMongoTest
public class MongoTest
{
#Autowired
private UserRepository userRepository;
#AfterEach
void cleanUpDatabase()
{
this.userRepository.deleteAll();
}
#Test
void bootstrapTestDataWithMongoTemplate() {
final var restaurant = new User( "123", "ABC", "DEF" );
this.userRepository.insert( restaurant );
final var found = this.userRepository.findByFirstName( "ABC" );
System.out.println( found );
}
}
UserRepository is an interface that extends MongoRepository. User itself is just a DBEntity with #Document annotation. If I let the SpringBoot application run, not in test mode, everything works fine, because MongoDB is running at the specified location. But for the test, I want to let it run as an in-memory DB.
But Springboot wants to connect for the test.
2022-01-20 08:31:57.489 INFO 3976 --- [ main] org.mongodb.driver.cluster : Cluster created with settings {hosts=[localhost:27017], mode=SINGLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms'}
2022-01-20 08:31:57.521 INFO 3976 --- [localhost:27017] org.mongodb.driver.cluster : Exception in monitor thread while connecting to server localhost:27017
com.mongodb.MongoSocketOpenException: Exception opening socket
at com.mongodb.internal.connection.SocketStream.open(SocketStream.java:70) ~[mongodb-driver-core-4.4.0.jar:na]
at com.mongodb.internal.connection.InternalStreamConnection.open(InternalStreamConnection.java:180) ~[mongodb-driver-core-4.4.0.jar:na]
at com.mongodb.internal.connection.DefaultServerMonitor$ServerMonitorRunnable.lookupServerDescription(DefaultServerMonitor.java:188) ~[mongodb-driver-core-4.4.0.jar:na]
at com.mongodb.internal.connection.DefaultServerMonitor$ServerMonitorRunnable.run(DefaultServerMonitor.java:152) ~[mongodb-driver-core-4.4.0.jar:na]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
Caused by: java.net.ConnectException: Connection refused: no further information
at java.base/sun.nio.ch.Net.pollConnect(Native Method) ~[na:na]
at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:672) ~[na:na]
at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:542) ~[na:na]
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:597) ~[na:na]
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327) ~[na:na]
at java.base/java.net.Socket.connect(Socket.java:633) ~[na:na]
at com.mongodb.internal.connection.SocketStreamHelper.initialize(SocketStreamHelper.java:107) ~[mongodb-driver-core-4.4.0.jar:na]
at com.mongodb.internal.connection.SocketStream.initializeSocket(SocketStream.java:79) ~[mongodb-driver-core-4.4.0.jar:na]
at com.mongodb.internal.connection.SocketStream.open(SocketStream.java:65) ~[mongodb-driver-core-4.4.0.jar:na]
... 4 common frames omitted
Any suggestions on how to do this? Is #DataMongoTest testing in the wrong way? I thought the integrated flapdoodle dependency would inject it automatically in the test case.
The pom itself is also not that complex:
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.github.openjson</groupId>
<artifactId>openjson</artifactId>
<version>1.0.11</version>
</dependency>
<dependency>
<groupId>com.github.erosb</groupId>
<artifactId>everit-json-schema</artifactId>
<version>1.14.0</version>
</dependency>
</dependencies>
The documentation states:
2.2.4. Embedded Mongo
Spring Boot offers auto-configuration for Embedded Mongo. To use it in
your Spring Boot application, add a dependency on
de.flapdoodle.embed:de.flapdoodle.embed.mongo and set the
spring.mongodb.embedded.version property to match the version of
MongoDB that your application will use in production. The default
download configuration allows access to most of the versions listed in
Embedded Mongo’s Version class as well as some others. Configuring an
inaccessible version will result in an error when attempting to
download the server. Such an error can be corrected by defining an
appropriately configured DownloadConfigBuilderCustomizer bean.
The port that Mongo listens on can be configured by setting the
spring.data.mongodb.port property. To use a randomly allocated free
port, use a value of 0. The MongoClient created by
MongoAutoConfiguration is automatically configured to use the randomly
allocated port. If you do not configure a custom port, the embedded
support uses a random port (rather than 27017) by default.
If you have SLF4J on the classpath, the output produced by Mongo is
automatically routed to a logger named
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongo.
You can declare your own IMongodConfig and IRuntimeConfig beans to
take control of the Mongo instance’s configuration and logging
routing. The download configuration can be customized by declaring a
DownloadConfigBuilderCustomizer bean.
Therefore add the below dependency in scope:test if you want it to only be applied for Tests.
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
As well you need to set the version in your application.properties file:
spring.mongodb.embedded.version=4.0.21
https://docs.spring.io/spring-boot/docs/current/reference/html/data.html#data.nosql.mongodb.embedded
Working Example:
<?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.6.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.mongodb.embedded</groupId>
<artifactId>mongodb-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mongodb-app</name>
<description>Demo project for usage of embedded mongodb</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>test</goal>
</goals>
<phase>integration-test</phase>
<configuration>
<excludes>
<exclude>none</exclude>
</excludes>
<includes>
<include>**/*IT.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
UserRepository:
public interface UserRepository extends MongoRepository<User, Long> {
//Spring converts this to Regex findByFirstnameRegex(String firstname) {"firstname" : {"$regex" : firstname }}
// automatically
public List<User> findByFirstName(String firstName);
}
User:
#Data
#Builder
public class User {
#Id
private Long id;
private String firstName;
private String lastName;
}
Test:
#DataMongoTest
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class UserControllerIT {
#Autowired
private UserRepository userRepository;
#BeforeAll
public void setup(){
userRepository.save(User.builder().id(1L).firstName("James").lastName("Bond").build());
userRepository.save(User.builder().id(2L).firstName("James").lastName("Farley").build());
userRepository.save(User.builder().id(3L).firstName("Marley").lastName("Hemp").build());
userRepository.save(User.builder().id(4L).firstName("James").lastName("Bond").build());
}
#Test
public void test_getById_successfull() throws Exception {
Assertions.assertEquals("James", userRepository.findByFirstName("James").get(0).getFirstName());
}
}
src/test/resources/application.properties
spring.data.mongodb.database=test
spring.data.mongodb.port=27017
spring.mongodb.embedded.version=4.0.2
When I try to deploy my .war project (Spring Boot) on a Tomcat server I get the following exception on the logs. Note that this exception doesn't happen locally, so I can only reproduce it on server enviroment.
Complete stacktrace https://pastebin.com/8uFqwk0U
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mappingComponent': Failed to introspect bean class [com.project.components.MappingComponent] for lookup method metadata: could not find class that it depends on; nested exception is java.lang.NoClassDefFoundError: java/time/temporal/Temporal
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:269) ~[spring-beans-4.3.5.RELEASE.jar:4.3.5.RELEASE]
I searched of course about this issue and I've changed some dependencies on ton pom.xml, also removed and added constructors just in case but I couldn't figure out why this is happening.
MappingComponent the calss that exception happens
#Component
public class MappingComponent {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
Repository1 repo1;
#Autowired
Repository2 repo2;
//no constructors
//methods that are using the repositories
}
MappingController the class that injects MappingComponent
#RestController
#CrossOrigin
#RequestMapping("/services")
public class MappingController {
#Autowired
private ClientComponent clientComponent;
#Autowired
private MappingComponent mappingComponent;
//no constructors
//services that are using the components
}
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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>services</groupId>
<artifactId>services</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>services</name>
<description>Services</description>
<packaging>war</packaging>
<properties>
<java.version>1.7</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-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</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.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Main class
#SpringBootApplication
public class ServicesApplication extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(ServicesApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(ServicesApplication.class, args);
}
}
I am aware that both java and spring are outdated, but it doesn't depend on me to upgrade them to a newer version. And that's not the issue because there are other services deployed with the same version (and identical pom.xml).
Temporal class has been introduced only in 1.8 and in your pom I see that you use 1.7.
When you are not sure of when a class has been introduced, just check in the Javadoc the #Since tag.
You might need to external the external dependency jar into the lib directory of the Tomcat web server. First shutdown tomcat, add jar to the lib directory, and then startup tomcat.
I'm trying to understand how to use #QuerydslPredicate but my test API fails when it is called:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: No primary or default constructor found for interface com.querydsl.core.types.Predicate] with root cause
java.lang.NoSuchMethodException: com.querydsl.core.types.Predicate.< init >()
This is the method in my Controller class:
#ResponseBody
#RequestMapping(value = "/user/query", method = RequestMethod.GET)
public Iterable<User> getByCriteria(#QuerydslPredicate(root = User.class) Predicate predicate)
{
return this.userDao.getByCriteria(predicate);
}
I've used this spring blog post and this example to try and implement my API but I don't understand what I am doing wrong.
Edit #1
pom.xml:
<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>email-encrypt</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
</parent>
<name>email-encrypt</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<querydsl.version>4.1.4</querydsl.version>
<springfox.version>2.6.1</springfox.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-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.59</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.59</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcmail-jdk15on</artifactId>
<version>1.59</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>${querydsl.version}</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>10.14.1.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${springfox.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${springfox.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>${build.directory}/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Edit #2
I've create a sample project here.
If you comment out the #Configuration on the swagger configuration class it will work. I'm still trying to understand why, I guess that with that annotation the way spring loads the configuration is different and this is causing the issue.
//#Configuration
#EnableSwagger2
public class SwaggerConfiguration extends WebMvcConfigurationSupport
I think your problem lies in your pom.xml. Be sure you are using compatible version of query-dsl. For instance if you use spring-data-jpa 2.0.8, you should use querydsl-* 4.1.4+
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-*</artifactId>
<version>4.1.4</version>
</dependency>
you can check in maven repository which version you need
Edit 1 try to add the querydsl-core to your maven:
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-core</artifactId>
<version>4.1.4</version>
</dependency>
Extending on #cristobalrosa's answer, this might be due to web application not being configured by Spring Boot. For instance my project also had a SwaggerConfig extending WebMvcConfigurationSupport:
#Configuration
#EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurationSupport {
// Docket bean
#Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
}
}
I removed the inheritance and manual resource handlers, and it works fine now.
Note: In addition to WebMvcConfigurationSupport, things like #EnableWebMvc & WebMvcConfigurer might also lead to Spring Boot's web autoconfiguration not being used.
Sources: Swagger Issue comment
Same error with me.
I just want to share the situation to may give some hints. So look at my config and dependencies and read articles that I linked. try #EnableWebMvc instead of 'WebMvcConfigurationSupport'
Error
java.lang.IllegalStateException: No primary or default constructor found for interface com.querydsl.core.types.Predicate
Caused by: java.lang.NoSuchMethodException: com.querydsl.core.types.Predicate.<init>()
at java.base/java.lang.Class.getConstructor0(Class.java:3427)
at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2631)
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.createAttribute(ModelAttributeMethodProcessor.java:216)
dependencies
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation "org.springframework.data:spring-data-commons"
implementation "com.querydsl:querydsl-apt:4.3.1"
implementation "com.querydsl:querydsl-jpa:4.3.1"
}
Querydsl web support is available in spring-data-commons since 1.11
from https://www.baeldung.com/rest-api-search-querydsl-web-in-spring-data-jpa
Web Mvc Config
In my case, I have to implements WebMvcConfigurer and add #EnableWebMvc instead of WebMvcConfigurationSupport. I don't know why #EnableWebMvc is needed even I had extended WebMvcConfigurationSupport with #Configuration. I just guess WebMvcConfigurationSupport doesn't implements init() of Predicate.
#Configuration
//#EnableSpringDataWebSupport // <-- doesn't necessary for me
#EnableSwagger2
#EnableWebMvc // <-- add
public class SwaggerConfig implements WebMvcConfigurer { //<-- instead of 'WebMvcConfigurationSupport'
...
}
QueryDSL web support From Spring document
The feature will be automatically enabled along #EnableSpringDataWebSupport when Querydsl is found on the classpath.
https://docs.spring.io/spring-data/jpa/docs/1.9.0.RELEASE/reference/html/#core.web.type-safe
Controller
#GetMapping
public List<InputMethodDto.Response> getInputMethodTypeList(
#QuerydslPredicate(root = InputMethod.class) Predicate predicate) {
return service.getInputMethodList(predicate);
}
Repository
public interface InputMethodRepository extends JpaRepository<yourEntity, Long>, QuerydslPredicateExecutor<yourEntity>, QuerydslBinderCustomizer<QyourEntity> {
#Override
default void customize(final QuerydslBindings bindings, final QyourEntity root) {
bindings.bind(String.class).first((StringPath path, String value)-> path.eq(value));
}
}