Swagger 2 with Spring REST API - whitelabel error page - rest

I am working on a simple HelloWorld Spring Boot application and I am trying to configure Swagger to automatically generate my REST service documentation.
I am following this tutorial: http://www.baeldung.com/swagger-2-documentation-for-spring-rest-api
But I am facing some difficulties.
I've added the following Swagger dependencies to the maven pom.xml file:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
and also the Java configuration class SwaggerConfig as follows:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}
According to the guide, this should be sufficient to view the generated documentation on http://localhost:8080/v2/api-docs
However, I am getting "Whitespace Error Page: This application has no explicit mapping for /error, so you are seeing this as a fallback."
I am getting the same error for the Swagger UI page at http://localhost:8080/greeting/api/swagger-ui.html too.
For extra information, my HelloWorld code is as follows:
package hello;
public class Greeting {
private final long id;
private final String content;
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
and the greeting controller is
package hello;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.atomic.AtomicLong;
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value="name", defaultValue="World") String name) {
return new Greeting(counter.incrementAndGet(),
String.format(template, name));
}
}
Everything is under main/java
I would really appreciate it if anyone has any ideas/suggestions on how to fix this! Thank you in advance :)

Related

Standalone SpringBatch reading from DB

Im very new to Spring batch, trying to make a standalone spring batch (Without springboot) that reading from MySql DB and write the results in CSV.
I tried googling and nothing worked out. I also can't get to understand the itemWriter/Itemreader creating are they just methods or classes inherit Writer/Reader? the read/write method what are their inputs/outputs ? I can't get any clear rules about this.
The springBatch can never be implanted without SpringBoot? if no so how?
Why I want standalone Springbatch cause I just want a main method to trigger the job. And I don't want a web app to call a certain endpoint to trigger the job.
The injection works without Springboot or its mandatory for the injection to work?
Also The jobLuncher is it injected or configured by xml files? cause sometimes I got error of jobLuancher is null after injecting it.
MY configuration file is :
import com.techtalk.debu.batch.entity.Employee;
import org.springframework.batch.core.*;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.database.JdbcCursorItemReader;
import org.springframework.batch.item.database.builder.JdbcCursorItemReaderBuilder;
import org.springframework.batch.item.file.FlatFileItemWriter;
import org.springframework.batch.item.file.builder.FlatFileItemWriterBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.FileSystemResource;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.jdbc.support.JdbcTransactionManager;
import javax.sql.DataSource;
#EnableBatchProcessing(dataSourceRef = "batchDataSource", transactionManagerRef = "batchTransactionManager")
public class LoadController {
#Bean
public DataSource batchDataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2)
.addScript("/org/springframework/batch/core/schema-h2.sql")
.generateUniqueName(false).build();
}
#Bean
public DataSource dataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.url("jdbc:mysql://localhost:3311/new");
dataSourceBuilder.username("sa");
dataSourceBuilder.password("12345678908");
return dataSourceBuilder.build();
}
#Bean
public JdbcTransactionManager batchTransactionManager(DataSource dataSource) {
return new JdbcTransactionManager(dataSource);
}
public static void main(String e []) throws Exception {
JobParameters jobParameters = new JobParametersBuilder().addLong("time", System.currentTimeMillis()).toJobParameters();
ApplicationContext context = new AnnotationConfigApplicationContext(LoadController.class);
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
JobExecution jobExecution = jobLauncher.run(context.getBean(Job.class), jobParameters);
System.out.println("Job Exit Status : " + jobExecution.getStatus());
}
#Bean
public Step step(JobRepository jobRepository, JdbcTransactionManager transactionManager) {
return new StepBuilder("step", jobRepository)
.<Employee, Employee>chunk(5, transactionManager)
.reader(itemReader())
.writer(itemWriter())
.processor(new CSVItemProcessor())
.build();
}
#Bean
public JdbcTransactionManager transactionManager(DataSource dataSource) {
return new JdbcTransactionManager(dataSource);
}
#Bean
public Job job(JobRepository jobRepository, JdbcTransactionManager transactionManager) {
return new JobBuilder("job")
.start(step(jobRepository, transactionManager))
.build();
}
#Bean
public JdbcCursorItemReader<Employee> itemReader() {
String sql = "select * from person";
return new JdbcCursorItemReaderBuilder<Employee>()
.name("personItemReader")
.dataSource(dataSource())
.sql(sql)
.beanRowMapper(Employee.class)
.build();
}
#Bean
public FlatFileItemWriter<Employee> itemWriter() {
return new FlatFileItemWriterBuilder<Employee>()
.resource(new FileSystemResource("persons.csv"))
.name("personItemWriter")
.delimited()
.names("id", "name")
.build();
}
}
and my pom file is:
<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>3.0.2</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.techtalk.debu</groupId>
<artifactId>Spring-Batch-CSV-to-DB-Project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-batch-CSV-To_DB-Write-Example-For-Beginners</name>
<description>Demo project for Beginners to understand Spring Boot</description>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-core</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-infrastructure</artifactId>
<version>5.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
when I run my code I got this error:
Failed to instantiate [org.springframework.batch.core.Job]: Factory
method 'job' threw exception with message:
java.lang.IllegalStateException: JobRepository must be set
Also I have some questions:
reader()/writer() this function should return what? what if I want the reader to read from another DataSource what should I do?
The data source defining in my application.properties by default is used to save job meta data or to apply the spring batch work?
You can run a standalone Spring Batch application without Spring Boot. The reference documentation contains a code sample for that. The javadoc of EnableBatchProcessing contains an example as well. You also have the samples which are not based on Spring Boot.
Here is a quick example for a non Boot standalone Spring Batch app with a main method:
package org.springframework.batch.sample;
import javax.sql.DataSource;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.database.JdbcCursorItemReader;
import org.springframework.batch.item.database.builder.JdbcCursorItemReaderBuilder;
import org.springframework.batch.item.file.FlatFileItemWriter;
import org.springframework.batch.item.file.builder.FlatFileItemWriterBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.jdbc.support.JdbcTransactionManager;
#Configuration
#EnableBatchProcessing
public class MyJobConfiguration {
#Bean
public JdbcCursorItemReader<Person> itemReader() {
String sql = "select * from person";
return new JdbcCursorItemReaderBuilder<Person>()
.name("personItemReader")
.dataSource(dataSource())
.sql(sql)
.beanRowMapper(Person.class)
.build();
}
#Bean
public FlatFileItemWriter<Person> itemWriter() {
return new FlatFileItemWriterBuilder<Person>()
.resource(new FileSystemResource("persons.csv"))
.name("personItemWriter")
.delimited()
.names("id", "name")
.build();
}
#Bean
public Job job(JobRepository jobRepository, JdbcTransactionManager transactionManager) {
return new JobBuilder("job", jobRepository)
.start(new StepBuilder("step", jobRepository)
.<Person, Person>chunk(5, transactionManager)
.reader(itemReader())
.writer(itemWriter())
.build())
.build();
}
#Bean
public DataSource dataSource() {
EmbeddedDatabase embeddedDatabase = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("/org/springframework/batch/core/schema-hsqldb.sql")
.build();
JdbcTemplate jdbcTemplate = new JdbcTemplate(embeddedDatabase);
jdbcTemplate.execute("create table person (id int primary key, name varchar(20));");
for (int i = 1; i <= 10; i++) {
jdbcTemplate.execute(String.format("insert into person values (%s, 'foo%s');", i, i));
}
return embeddedDatabase;
}
#Bean
public JdbcTransactionManager transactionManager(DataSource dataSource) {
return new JdbcTransactionManager(dataSource);
}
static class Person {
private int id;
private String name;
public Person() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return "Person{id=" + id + ", name='" + name + '\'' + '}';
}
}
public static void main(String[] args) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(MyJobConfiguration.class);
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
Job job = context.getBean(Job.class);
jobLauncher.run(job, new JobParameters());
}
}
You just need to make sure to use Spring Batch 5.0.0 and have Hsqldb in the classpath.
EDIT: based on question update
rying to make a standalone spring batch (Without springboot) that reading from MySql DB and write the results in CSV.
I updated the sample to something like what you are trying to do. You just need to update the datasource to point to your MySQL database.
reader()/writer() this function should return what?
Those should return a type implementing ItemReader and ItemWriter repectively.
what if I want the reader to read from another DataSource what should I do?
In this case, you can configure your reader to read from that datasource, as shown in the example above. The datasource could be different from the one storing the batch meta-data.
CSVItemProcessor is a class implementing ItemProcessor <Employee, Employee> and having the process method but the reader not accepted this code
Your CSVItemProcessor should implement ItemProcessor<Employee, Employee> and your step should be declared as follows:
#Bean
public Step step() {
return new StepBuilder("MY Step")
.<Employee, Employee>chunk(10) // note the explicit input and output type definition here, which is missing in your code
.reader(reader())
.processor(new CSVItemProcessor())
.writer(writer())
.build();
}
Moreover, you need to pass the job repository and transaction manager to the step as shown in the complete example above, something like:
#Bean
public Step step(JobRepository jobRepository, JdbcTransactionManager transactionManager) {
return new StepBuilder("MY Step", jobRepository)
.<Employee, Employee>chunk(10, transactionManager) // note the explicit input and output type definition here, which is missing in your code
.reader(reader())
.processor(new CSVItemProcessor())
.writer(writer())
.build();
}

Using embedded mongo in spring for local testing only

I'm trying to use the flapdoodle embedded mongo database ('de.flapdoodle.embed:de.flapdoodle.embed.mongo')
for local manual testing the way I'd use H2 for relational database testing. I only want the embedded mongo to start up when running under the 'local' SPring profile, but I can't figure out how to make it happen -- it just always starts under any profile.
I've tried adding #SpringBootApplication(exclude=EmbeddedMongoAutoConfiguration.class) to my application (thinking I could reimport that autoconfig to a single profile later) but that doesn't seem to have any affect.
Anyone know how to limit it to a single profile, in a way that will work when running in Eclipse as well as via Gradle?
I can think of two ways:
1- Load your dependency only in specific profile in maven or gradle
Maven
<profiles>
<profile>
<id>local</id>
<dependencies>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>2.0.4-SNAPSHOT</version>
</dependency>
</dependencies>
</profile>
</profiles>
Gradle
if (project.hasProperty('local')) {
dependencies {
compile 'de.flapdoodle.embed:de.flapdoodle.embed.mongo:2.0.4-SNAPSHOT'
}
}
2- Using #Profile and #Import in your class
LoadEmbeded.java
#Profile(value = "local")
#Configuration
#Import(EmbeddedMongoAutoConfiguration.class)
public class LoadEmbeded {
}
Hope it can give your a idea about it
Edit 1:
I have tested my approach and made a very simple application. It works with #Profile. Here is what I tested.
#SpringBootApplication(exclude = EmbeddedMongoAutoConfiguration.class)
public class DemoApplication {
#Autowired
TestRepository repository;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
CommandLineRunner runner() {
return args -> {
System.out.println(repository.save(new Test()));
System.out.println(repository.findAll());
};
}
}
#Repository
interface TestRepository extends MongoRepository<Test, String> {
}
#Document
class Test {
#Id
private String id;
public String getId() {
return id;
}
public Test setId(String id) {
this.id = id;
return this;
}
#Override
public String toString() {
return new ToStringBuilder(this)
.append("id", id)
.toString();
}
}
#Configuration
#Profile("local")
#Import(EmbeddedMongoAutoConfiguration.class)
class Load {
}
And application.properties
spring.profiles.active=local
When I changed active profile to something else but local, it throws exception during startup and complaining about connection of mongodb. But when I set it local, it works and shows me the id of saved test entity.
If still you are experiencing same problem, maybe one of your dependencies is loading the Embedded MongoDB again even you exclude it. Usually it should be with test dependencies. Check your dependencies.

How to used "web request by spring mvc" and "rest by jersey" in spring boot

Hi i want to handle web request by spring mvc and handle rest by jersey
in the same project (Spring-Boot)
As i test Rest service is working but web is not
How can i set Application Config ?
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan(basePackageClasses = {ProductsResource.class, MessageService.class,Web.class})
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Bean
public ServletRegistrationBean jerseyServlet() {
ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/rest/*");
registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyInitialization.class.getName());
return registration;
}
#Bean
public ServletRegistrationBean webMVC() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(ResourceConfig.class);
dispatcherServlet.setApplicationContext(applicationContext);
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet, "*.html");
servletRegistrationBean.setName("web-mvc");
return servletRegistrationBean;
}
Web Controller
#Controller
#Component
public class Web {
#RequestMapping("/foo")
String foo() {
return "foo";
}
#RequestMapping("/bar")
String bar() {
return "bar";
}
}
Rest Controller
#Path("/")
#Component
public class ProductsResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("/hello")
public String hello() {
return "Hello World";
}
}
Actually with spring boot (I'm talking about version 1.4.x), it's pretty easy to do Spring MVC and JAX-RS(rest by jersey) at the same time :). You don't need any servlet registrations. All you need to do is to add a Configuration class like the following
#Configuration
#ApplicationPath("/rest")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("your.package.with.rest.resources");
}
}
Now all your JAXRS resources are served under /rest/*
For example, your Rest Controller can be refactored as
#Path("/hello")
#Component
public class ProductsResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public String hello() {
return "Hello World";
}
}
Now if you hit url http://server:port/rest/hello, Hello World should be returned.
Finally, remember to add the following dependencies in your maven pom file
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
That should work for you.

How to unit test a Spring Boot MongoRepository?

In my Spring Boot web application I use MongoDB to store data. In the application I access the database using interfaces that extend MongoRepository.
How do I set up a unit test for such a repository class? What I would like is to
start an embedded/in memory instance of MongoDB
insert testdata from JSON or XML
use an autowired repository to perform queries on the testdata
I have tried using Embedded MongoDB, but I can't figure out how to insert testdata from a file.
I've also tried using NoSQLUnit, but the SpringApplicationConfiguration conflicts with the unit test configuration, resulting in different databases for reading and writing.
Just use #DataMongoTest from Spring Boot.
#RunWith(SpringRunner.class)
#DataMongoTest
public class FooRepositoryTest {
#Autowired
FooRepository fooRepository;
#Before
public void setUp() throws Exception {
fooRepository.save(new Foo());
}
#Test
public void shouldBeNotEmpty() {
assertThat(fooRepository.findAll()).isNotEmpty();
}
}
Dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
An update for 2018 and Spring Boot 2. First of all, you can have data-only integration tests with the embedded db as per the documentation. This uses the DataMongoTest annotation. This configures only the necessary dependencies that make mongoDB tests possible.
If you want to do full integration tests, add the AutoConfigureDataMongo annotation instead:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#AutoConfigureDataMongo
public class PriceApiControllerIT {
Dependencies you should have in your pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<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-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
This is what I did.
public interface UserRepository extends MongoRepository<Users, Long> {
public void deleteByUserId(String userId);
public List<Users> findAllByUserStatus(String userStatus);
}
#Document
public class Users {
#Id
private long id;
#Transient
public static final String SEQUENCE_NAME = "users_sequence";
#Indexed
#NotNull
private String userId;
private String firstName;
private String lastName;
private String userType;
private String userStatus;
#Email
private String emailId;
#Size(min = 10, max = 10)
#NumberFormat
private String phoneNumber;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getUserType() {
return userType;
}
public void setUserType(String userType) {
this.userType = userType;
}
public String getEmailId() {
return emailId;
}
public void setEmailId(String emailId) {
this.emailId = emailId;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public static String getSequenceName() {
return SEQUENCE_NAME;
}
public String getUserStatus() {
return userStatus;
}
public void setUserStatus(String userStatus) {
this.userStatus = userStatus;
}
}
Here is the junit
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = MockodsApplication.class)
#SpringBootTest
#AutoConfigureMockMvc
public class UserControllerIT {
#Autowired
private UserRepository userRepository;
#Autowired
MongoTemplate mongoTemplate;
#Autowired
private MockMvc mvc;
#After
public void tearDown() {
}
#Test
public void test1() {
Users user = new Users();
long userId = 1L;
user.setId(userId);
user.setFirstName("FirstName");
user.setLastName("FirstName");
user.setEmailId("fisrtname.secondname#gmail.com");
user.setPhoneNumber("1234567890");
assertEquals(user, userRepository.save(user));
}
#Test
public void test2() {
List<Users> persistedUser = userRepository.findAll();
assertEquals("fisrtname.secondname#gmail.com", persistedUser.get(0).getEmailId());
}
}
This link helped me to implement https://dzone.com/articles/spring-integration-tests
I faced the same problem and we used a separate MongoConfiguration class to specify a particular configuration for our tests.
You can create an embedded mongo instance by using EmbeddedMongoBuilder like this :
import com.mongodb.Mongo;
import cz.jirutka.spring.embedmongo.EmbeddedMongoBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.net.ServerSocket;
#Configuration
public class MongoConfiguration {
#Bean
public Mongo mongo() throws IOException {
System.setProperty("DB.TRACE","true");
return new EmbeddedMongoBuilder()
.version("2.13.1")
.bindIp("127.0.0.1")
.port(allocateRandomPort())
.build();
}
}
Then in your test class, specify that you want to use that particular configuration with the #Import annotation :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#Import(MongoConfiguration.class)
Hope that helps.
Check out https://mongoUnit.org
From its docs:
It's a data driven Integration testing framework for Spring Boot
based applications that use MongoDB for persistence. The framework
enables the developer to test the data access logic with relative
ease.
First, make sure that you have added the following Spring Boot parent to your project:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
Since we added Spring Boot parent, we can add required dependencies without specifying their versions:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
spring-boot-starter-data-mongodb will enable Spring support for
MongoDB
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
de.flapdoodle.embed.mongo provides embedded MongoDB for integration
tests
After adding de.flapdoodle.embed.mongo dependency Spring Boot will automatically try to download and start the embedded MongoDB when running tests. The following snippet shows how you can configure the embedded MongoDB instance manually
class EmbeddedMongoDbIntegrationTest {
private MongodExecutable mongodExecutable;
private MongoTemplate mongoTemplate;
#After
void clean() {
mongodExecutable.stop();
}
#Before
void setup() throws Exception {
String ip = "localhost";
int port = 27017;
IMongodConfig mongodConfig = new MongodConfigBuilder().version(Version.Main.PRODUCTION)
.net(new Net(ip, port, Network.localhostIsIPv6()))
.build();
MongodStarter starter = MongodStarter.getDefaultInstance();
mongodExecutable = starter.prepare(mongodConfig);
mongodExecutable.start();
mongoTemplate = new MongoTemplate(new MongoClient(ip, port), "test");
}
#Test
void test() throws Exception {
// given
DBObject objectToSave = BasicDBObjectBuilder.start()
.add("key", "value")
.get();
// when
mongoTemplate.save(objectToSave, "collection");
// then
assertThat(mongoTemplate.findAll(DBObject.class, "collection")).extracting("key")
.containsOnly("value");
}
}
Note, that we can quickly create MongoTemplate bean configured to use
our manually configured embedded database and register it inside the
Spring container, so your mongo repository will start leveraging this
mongoTemplate as well.

"404 Not Found" when viewing swagger api-docs when using swagger-springmvc (now springfox)

I am trying to configure swagger in my spring project, but hitting Hitting "http://localhost:8080/api-docs" says "404 Not Found".
Maven Dependency
<dependency>
<groupId>com.mangofactory</groupId>
<artifactId>swagger-springmvc</artifactId>
<version>0.8.2</version>
</dependency>
SwaggerConfig.java File
package com.ucap.swagger;
import com.mangofactory.swagger.configuration.JacksonScalaSupport;
import com.mangofactory.swagger.configuration.SpringSwaggerConfig;
import com.mangofactory.swagger.configuration.SpringSwaggerModelConfig;
import com.mangofactory.swagger.configuration.SwaggerGlobalSettings;
import com.mangofactory.swagger.core.DefaultSwaggerPathProvider;
import com.mangofactory.swagger.core.SwaggerApiResourceListing;
import com.mangofactory.swagger.core.SwaggerPathProvider;
import com.mangofactory.swagger.scanners.ApiListingReferenceScanner;
import com.wordnik.swagger.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
#Configuration
#ComponentScan(basePackages = "com.mangofactory.swagger")
public class SwaggerConfig {
public static final List<String> DEFAULT_INCLUDE_PATTERNS = Arrays.asList("/news/.*");
public static final String SWAGGER_GROUP = "mobile-api";
#Value("${app.docs}")
private String docsLocation;
#Autowired
private SpringSwaggerConfig springSwaggerConfig;
#Autowired
private SpringSwaggerModelConfig springSwaggerModelConfig;
/**
* Adds the jackson scala module to the MappingJackson2HttpMessageConverter registered with spring
* Swagger core models are scala so we need to be able to convert to JSON
* Also registers some custom serializers needed to transform swagger models to swagger-ui required json format
*/
#Bean
public JacksonScalaSupport jacksonScalaSupport() {
JacksonScalaSupport jacksonScalaSupport = new JacksonScalaSupport();
//Set to false to disable
jacksonScalaSupport.setRegisterScalaModule(true);
return jacksonScalaSupport;
}
/**
* Global swagger settings
*/
#Bean
public SwaggerGlobalSettings swaggerGlobalSettings() {
SwaggerGlobalSettings swaggerGlobalSettings = new SwaggerGlobalSettings();
swaggerGlobalSettings.setGlobalResponseMessages(springSwaggerConfig.defaultResponseMessages());
swaggerGlobalSettings.setIgnorableParameterTypes(springSwaggerConfig.defaultIgnorableParameterTypes());
swaggerGlobalSettings.setParameterDataTypes(springSwaggerModelConfig.defaultParameterDataTypes());
return swaggerGlobalSettings;
}
/**
* API Info as it appears on the swagger-ui page
*/
private ApiInfo apiInfo() {
ApiInfo apiInfo = new ApiInfo(
"News API",
"Mobile applications and beyond!",
"https://helloreverb.com/terms/",
"matt#raibledesigns.com",
"Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0.html"
);
return apiInfo;
}
/**
* Configure a SwaggerApiResourceListing for each swagger instance within your app. e.g. 1. private 2. external apis
* Required to be a spring bean as spring will call the postConstruct method to bootstrap swagger scanning.
*
* #return
*/
#Bean
public SwaggerApiResourceListing swaggerApiResourceListing() {
//The group name is important and should match the group set on ApiListingReferenceScanner
//Note that swaggerCache() is by DefaultSwaggerController to serve the swagger json
SwaggerApiResourceListing swaggerApiResourceListing = new SwaggerApiResourceListing(springSwaggerConfig.swaggerCache(), SWAGGER_GROUP);
//Set the required swagger settings
swaggerApiResourceListing.setSwaggerGlobalSettings(swaggerGlobalSettings());
//Use a custom path provider or springSwaggerConfig.defaultSwaggerPathProvider()
swaggerApiResourceListing.setSwaggerPathProvider(apiPathProvider());
//Supply the API Info as it should appear on swagger-ui web page
swaggerApiResourceListing.setApiInfo(apiInfo());
//Global authorization - see the swagger documentation
swaggerApiResourceListing.setAuthorizationTypes(authorizationTypes());
//Every SwaggerApiResourceListing needs an ApiListingReferenceScanner to scan the spring request mappings
swaggerApiResourceListing.setApiListingReferenceScanner(apiListingReferenceScanner());
return swaggerApiResourceListing;
}
#Bean
/**
* The ApiListingReferenceScanner does most of the work.
* Scans the appropriate spring RequestMappingHandlerMappings
* Applies the correct absolute paths to the generated swagger resources
*/
public ApiListingReferenceScanner apiListingReferenceScanner() {
ApiListingReferenceScanner apiListingReferenceScanner = new ApiListingReferenceScanner();
//Picks up all of the registered spring RequestMappingHandlerMappings for scanning
apiListingReferenceScanner.setRequestMappingHandlerMapping(springSwaggerConfig.swaggerRequestMappingHandlerMappings());
//Excludes any controllers with the supplied annotations
apiListingReferenceScanner.setExcludeAnnotations(springSwaggerConfig.defaultExcludeAnnotations());
//
apiListingReferenceScanner.setResourceGroupingStrategy(springSwaggerConfig.defaultResourceGroupingStrategy());
//Path provider used to generate the appropriate uri's
apiListingReferenceScanner.setSwaggerPathProvider(apiPathProvider());
//Must match the swagger group set on the SwaggerApiResourceListing
apiListingReferenceScanner.setSwaggerGroup(SWAGGER_GROUP);
//Only include paths that match the supplied regular expressions
apiListingReferenceScanner.setIncludePatterns(DEFAULT_INCLUDE_PATTERNS);
return apiListingReferenceScanner;
}
/**
* Example of a custom path provider
*/
#Bean
public ApiPathProvider apiPathProvider() {
ApiPathProvider apiPathProvider = new ApiPathProvider(docsLocation);
apiPathProvider.setDefaultSwaggerPathProvider(springSwaggerConfig.defaultSwaggerPathProvider());
return apiPathProvider;
}
private List<AuthorizationType> authorizationTypes() {
ArrayList<AuthorizationType> authorizationTypes = new ArrayList<>();
List<AuthorizationScope> authorizationScopeList = newArrayList();
authorizationScopeList.add(new AuthorizationScope("global", "access all"));
List<GrantType> grantTypes = newArrayList();
LoginEndpoint loginEndpoint = new LoginEndpoint(apiPathProvider().getAppBasePath() + "/user/authenticate");
grantTypes.add(new ImplicitGrant(loginEndpoint, "access_token"));
return authorizationTypes;
}
#Bean
public SwaggerPathProvider relativeSwaggerPathProvider() {
return new ApiRelativeSwaggerPathProvider();
}
private class ApiRelativeSwaggerPathProvider extends DefaultSwaggerPathProvider {
#Override
public String getAppBasePath() {
return "/";
}
#Override
public String getSwaggerDocumentationBasePath() {
return "/api-docs";
}
}
}
ApiPathProvider.java file
package com.ucap.swagger;
import com.mangofactory.swagger.core.SwaggerPathProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.util.UriComponentsBuilder;
import javax.servlet.ServletContext;
public class ApiPathProvider implements SwaggerPathProvider {
private SwaggerPathProvider defaultSwaggerPathProvider;
#Autowired
private ServletContext servletContext;
private String docsLocation;
public ApiPathProvider(String docsLocation) {
this.docsLocation = docsLocation;
}
#Override
public String getApiResourcePrefix() {
return defaultSwaggerPathProvider.getApiResourcePrefix();
}
public String getAppBasePath() {
return UriComponentsBuilder
.fromHttpUrl(docsLocation)
.path(servletContext.getContextPath())
.build()
.toString();
}
#Override
public String getSwaggerDocumentationBasePath() {
return UriComponentsBuilder
.fromHttpUrl(getAppBasePath())
.pathSegment("api-docs/")
.build()
.toString();
}
#Override
public String getRequestMappingEndpoint(String requestMappingPattern) {
return defaultSwaggerPathProvider.getRequestMappingEndpoint(requestMappingPattern);
}
public void setDefaultSwaggerPathProvider(SwaggerPathProvider defaultSwaggerPathProvider) {
this.defaultSwaggerPathProvider = defaultSwaggerPathProvider;
}
}
Also, there is a property file in resources folder which contains:
app.docs=http://localhost:8080
When I am hitting "http://localhost:8080/api-docs" it neither gives any result nor any error, On google Rest the response code is 404.
Please help me on this.
The version that you're using (0.8.2) is a very old version of springfox. You should try and move to the latest released version, which is 2.0.
To answer your questions specifically.
Move to the most recent pre-2.0 version. Change the version of swagger-spingmvc in your pom to 1.0.2
The swagger configuration is vastly simplified in later releases. Change your SwaggerConfig to look like this.
#Configuration
//Hard to tell without seeing all your configuration, but optionally,
//add EnableWebMvc annotation in case its not working
#EnableWebMvc
#EnableSwagger
//Assuming your controllers are in this package
#ComponentScan(basePackages = "com.ucap.swagger")
public class SwaggerConfig {
public static final List<String> DEFAULT_INCLUDE_PATTERNS
= Arrays.asList("/news/.*");
//Unless you specifically want this group, I would recommend ignoring this
public static final String SWAGGER_GROUP = "mobile-api";
#Autowired
private SpringSwaggerConfig springSwaggerConfig;
#Bean
public SwaggerSpringMvcPlugin customImplementation(){
return new SwaggerSpringMvcPlugin(this.springSwaggerConfig)
.includePatterns(DEFAULT_INCLUDE_PATTERNS);
}
}
You don't need your path provider any more.
You no longer need the property file