Spring Boot Interview Questions

Spring Boot

Spring Boot is an open source Java-based framework used to create a micro Service. It is developed by Pivotal Team and is used to build stand-alone and production ready spring applications.

Spring Boot is designed with the following goals −

·         To avoid complex XML configuration in Spring

·         To develop a production ready Spring applications in an easier way

·         To reduce the development time and run the application independently

·         Offer an easier way of getting started with the application

 

Why Spring Boot

·         It provides a flexible way to configure Java Beans, XML configurations, and Database Transactions.

·         It provides a powerful batch processing and manages REST endpoints.

·         In Spring Boot, everything is auto configured; no manual configurations are needed.

·         It offers annotation-based spring application

·         Eases dependency management

·         It includes Embedded Servlet Container

 

What is Micro Service?

Micro Service is an architecture that allows the developers to develop and deploy services independently. Each service running has its own process and this achieves the lightweight model to support business applications.

Advantages

·         Micro services offers the following advantages to its developers −

·         Easy deployment

·         Simple scalability

·         Compatible with Containers

·         Minimum configuration

·         Lesser production time

 

The entry point of the spring boot application is the class contains @SpringBootApplication annotation and the main method.

Spring Boot automatically scans all the components included in the project by using @ComponentScan annotation.

 

Spring Boot Application

The entry point of the Spring Boot Application is the class contains @SpringBootApplication annotation. This class should have the main method to run the Spring Boot application. 

@SpringBootApplicationannotation includes Auto- Configuration, Component Scan, and Spring Boot Configuration.

If you added @SpringBootApplication annotation to the class, you do not need to add the @EnableAutoConfiguration, @ComponentScan and @SpringBootConfiguration annotation. The @SpringBootApplicationannotation includes all other annotations.

 

Component Scan

Spring Boot application scans all the beans and package declarations when the application initializes. You need to add the @ComponentScan annotation for your class file to scan your components added in your project.


The @Value annotation is used to read the environment or application property value in Java code. The syntax to read the property value is shown below −

@Value("${property_key_name}")

 

Spring Boot provides a very good support to building RESTful Web Services for enterprise applications.

<dependency>

  <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-starter-web</artifactId>   

</dependency>

Rest Controller

The @RestController annotation is used to define the RESTful web services. It serves JSON, XML and custom response. Its syntax is shown below −

@RestController
public class ProductServiceController { 
}

Request Mapping

The @RequestMapping annotation is used to define the Request URI to access the REST Endpoints. We can define Request method to consume and produce object. The default request method is GET.

@RequestMapping(value = "/products")
public ResponseEntity<Object> getProducts() { }

Request Body

The @RequestBody annotation is used to define the request body content type.

public ResponseEntity<Object> createProduct(@RequestBody Product product) {
}

Path Variable

The @PathVariable annotation is used to define the custom or dynamic request URI. The Path variable in request URI is defined as curly braces {} as shown below −

public ResponseEntity<Object> updateProduct(@PathVariable("id") String id) {
}

Request Parameter

The @RequestParam annotation is used to read the request parameters from the Request URL. By default, it is a required parameter. We can also set default value for request parameters as shown here −

public ResponseEntity<Object> getProduct(
   @RequestParam(value = "name", required = false, defaultValue = "honey") String name) {
}

 

GET API

The default HTTP request method is GET. This method does not require any Request Body. You can send request parameters and path variables to define the custom or dynamic URL.

@RestController
public class ProductServiceController {
   private static Map<String, Product> productRepo = new HashMap<>();
   static {
      Product honey = new Product();
      honey.setId("1");
      honey.setName("Honey");
      productRepo.put(honey.getId(), honey);
   }
   @RequestMapping(value = "/products")
   public ResponseEntity<Object> getProduct() {
      return new ResponseEntity<>(productRepo.values(), HttpStatus.OK);
   }

 

POST API

The HTTP POST request is used to create a resource. This method contains the Request Body. We can send request parameters and path variables to define the custom or dynamic URL.

@RestController
public class ProductServiceController {
   private static Map<String, Product> productRepo = new HashMap<>();
   @RequestMapping(value = "/products", method = RequestMethod.POST)
   public ResponseEntity<Object> createProduct(@RequestBody Product product) {
      productRepo.put(product.getId(), product);
      return new ResponseEntity<>("Product is created successfully", HttpStatus.CREATED);
   }
}

PUT API

The HTTP PUT request is used to update the existing resource. This method contains a Request Body. We can send request parameters and path variables to define the custom or dynamic URL.

@RestController
public class ProductServiceController {
   private static Map<String, Product> productRepo = new HashMap<>();
   
   @RequestMapping(value = "/products/{id}", method = RequestMethod.PUT)
   public ResponseEntity<Object> updateProduct(@PathVariable("id") String id, @RequestBody Product product) { 
      productRepo.remove(id);
      product.setId(id);
      productRepo.put(id, product);
      return new ResponseEntity<>("Product is updated successsfully", HttpStatus.OK);
   }   
}

DELETE API

The HTTP Delete request is used to delete the existing resource. This method does not contain any Request Body. We can send request parameters and path variables to define the custom or dynamic URL.

@RestController
public class ProductServiceController {
   private static Map<String, Product> productRepo = new HashMap<>();
   
   @RequestMapping(value = "/products/{id}", method = RequestMethod.DELETE)
   public ResponseEntity<Object> delete(@PathVariable("id") String id) { 
      productRepo.remove(id);
      return new ResponseEntity<>("Product is deleted successsfully", HttpStatus.OK);
   }
}

Spring Boot Exception Handling

Handling exceptions and errors in APIs and sending the proper response to the client is good for enterprise applications. 

Controller Advice

The @ControllerAdvice is an annotation, to handle the exceptions globally.

Exception Handler

The @ExceptionHandler is an annotation used to handle the specific exceptions and sending the custom responses to the client.

@ControllerAdvice
   public class ProductExceptionController {
}
 
public class ProductNotfoundException extends RuntimeException {
   private static final long serialVersionUID = 1L;
}
@ExceptionHandler(value = ProductNotfoundException.class)
public ResponseEntity<Object> exception(ProductNotfoundException exception) {
}
@RequestMapping(value = "/products/{id}", method = RequestMethod.PUT)
public ResponseEntity<Object> updateProduct() { 
   throw new ProductNotfoundException();
}

Spring Boot - Interceptors

You can use the Interceptor in Spring Boot to perform operations under the following situations −

·        Before sending the request to the controller

·        Before sending the response to the client

For example, you can use an interceptor to add the request header before sending the request to the controller and add the response header before sending the response to the client.

To work with interceptor, you need to create @Component class that supports it and it should implement the HandlerInterceptor interface.

The following are the three methods you should know about while working on Interceptors −

·         preHandle() method − This is used to perform operations before sending the request to the controller. This method should return true to return the response to the client.

·         postHandle() method − This is used to perform operations before sending the response to the client.

·         afterCompletion() method − This is used to perform operations after completing the request and response.

 

@Component

public class ProductServiceInterceptor implements HandlerInterceptor {

 

   @Override

   public boolean preHandle(

      HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

      return true;

   }

 

   @Override

   public void postHandle(

      HttpServletRequest request, HttpServletResponse response, Object handler,

      ModelAndView modelAndView) throws Exception {}

 

   @Override

   public void afterCompletion(HttpServletRequest request, HttpServletResponse response,

      Object handler, Exception exception) throws Exception {}

}

 

You will have to register this Interceptor with InterceptorRegistry by using WebMvcConfigurerAdapter as shown below –

@Component

public class ProductServiceInterceptorAppConfig extends WebMvcConfigurerAdapter {

   @Autowired

   ProductServiceInterceptor productServiceInterceptor;

 

   @Override

   public void addInterceptors(InterceptorRegistry registry) {

      registry.addInterceptor(productServiceInterceptor);

   }

}

 

A filter is an object used to intercept the HTTP requests and responses of your application. By using filter, we can perform two operations at two instances −

·         Before sending the request to the controller

·         Before sending a response to the client.

 

The following code shows the sample code for a Servlet Filter implementation class with @Component annotation.

 

@Component

public class SimpleFilter implements Filter {

   @Override

   public void destroy() {}

 

   @Override

   public void doFilter

      (ServletRequest request, ServletResponse response, FilterChain filterchain)

      throws IOException, ServletException {}

 

   @Override

   public void init(FilterConfig filterconfig) throws ServletException {}

}

 

Spring Boot-Rest Template

Rest Template is used to create applications that consume RESTful Web Services. You can use the exchange() method to consume the web services for all HTTP methods. 

@SpringBootApplication

public class DemoApplication {

   public static void main(String[] args) {

      SpringApplication.run(DemoApplication.class, args);

   }

   @Bean

   public RestTemplate getRestTemplate() {

      return new RestTemplate();

   }

}

 

You will have to follow the given points to consume the API −

·         Autowired the Rest Template Object.

·         Use HttpHeaders to set the Request Headers.

·         Use HttpEntity to wrap the request object.

·         Provide the URL, HttpMethod, and Return type for Exchange() method.

 

@RestController

public class ConsumeWebService {

   @Autowired

   RestTemplate restTemplate;

 

   @RequestMapping(value = "/template/products")

   public String getProductList() {

      HttpHeaders headers = new HttpHeaders();

      headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));

      HttpEntity <String> entity = new HttpEntity<String>(headers);

     

      return restTemplate.exchange("

         http://localhost:8080/products", HttpMethod.GET, entity, String.class).getBody();

   }

}

File Upload

For uploading a file, you can use MultipartFile as a Request Parameter and this API should consume Multi-Part form data value.

@RestController

public class FileUploadController {

   @RequestMapping(value = "/upload", method = RequestMethod.POST,

      consumes = MediaType.MULTIPART_FORM_DATA_VALUE)

   public String fileUpload(@RequestParam("file") MultipartFile file) throws IOException {

      File convertFile = new File("/var/tmp/"+file.getOriginalFilename());

      convertFile.createNewFile();

      FileOutputStream fout = new FileOutputStream(convertFile);

      fout.write(file.getBytes());

      fout.close();

      return "File is upload successfully";

   }

}

 

File Download

For file download, you should use InputStreamResource for downloading a File. We need to set the HttpHeader Content-Disposition in Response and need to specify the response Media Type of the application.

@RequestMapping(value = "/download", method = RequestMethod.GET)

public ResponseEntity<Object> downloadFile() throws IOException  {

   String filename = "/var/tmp/mysql.png";

   File file = new File(filename);

   InputStreamResource resource = new InputStreamResource(new FileInputStream(file));

   HttpHeaders headers = new HttpHeaders();

     

   headers.add("Content-Disposition", String.format("attachment; filename=\"%s\"", file.getName()));

   headers.add("Cache-Control", "no-cache, no-store, must-revalidate");

   headers.add("Pragma", "no-cache");

   headers.add("Expires", "0");

     

   ResponseEntity<Object>

   responseEntity = ResponseEntity.ok().headers(headers).contentLength(file.length()).contentType(

      MediaType.parseMediaType("application/txt")).body(resource);

     

   return responseEntity;

}

 

Service Components are the class file which contains @Service annotation. These class files are used to write business logic in a different layer, separated from @RestController class file.

@Service
public class ProductServiceImpl implements ProductService {
}

 In the HTML file, we added the jQuery library and written the code that submits the form to RESTful web service on clicking the button.

<script src = "https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

<script>

   $(document).ready(function() {

      $("button").click(function() {

         var productmodel = {

            id : "3",

            name : "Ginger"

         };

         var requestJSON = JSON.stringify(productmodel);

         $.ajax({

            type : "POST",

            url : "http://localhost:9090/products",

            headers : {

               "Content-Type" : "application/json"

            },

            data : requestJSON,

            success : function(data) {

               alert(data);

            },

            error : function(data) {

            }

         });

      });

   });

</script>

 

Cross-Origin Resource Sharing (CORS) is a security concept that allows restricting the resources implemented in web browsers. It prevents the JavaScript code producing or consuming the requests against different origin.

We need to set the origins for RESTful web service by using @CrossOriginannotation for the controller method. This @CrossOrigin annotation supports specific REST API, and not for the entire application.

@RequestMapping(value = "/products")
@CrossOrigin(origins = "http://localhost:8080")
 
public ResponseEntity<Object> getProduct() {
   return null;
}

Spring Boot-Eureka Server

Eureka Server is an application that holds the information about all client-service applications. Every Micro service will register into the Eureka server and Eureka server knows all the client applications running on each port and IP address. Eureka Server is also known as Discovery Server.

<dependency>

<groupId>org.springframework.cloud</groupId>

   <artifactId>spring-cloud-starter-eureka-server</artifactId>

</dependency>

 

@SpringBootApplication

@EnableEurekaServer

public class EurekaserverApplication {

   public static void main(String[] args) {

      SpringApplication.run(EurekaserverApplication.class, args);

   }

}

 

Application.properties

eureka.client.registerWithEureka = false

eureka.client.fetchRegistry = false

server.port = 8761

 

By default, the Eureka Server registers itself into the discovery. 

How to register the Spring Boot Micro service application into the Eureka Server.

Before registering the application, please make sure Eureka Server is running on the port 8761 or first build the Eureka Server and run it.

<dependency>

   <groupId>org.springframework.cloud</groupId>

   <artifactId>spring-cloud-starter-eureka</artifactId>

</dependency>

 

@SpringBootApplication

@EnableEurekaClient

public class EurekaclientApplication {

   public static void main(String[] args) {

      SpringApplication.run(EurekaclientApplication.class, args);

   }

}

 

To register the Spring Boot application into Eureka Server we need to add the following configuration in our application.properties file.

eureka.client.serviceUrl.defaultZone  = http://localhost:8761/eureka

eureka.client.instance.preferIpAddress = true

spring.application.name = eurekaclient

 

Now, add the Rest Endpoint to return String in the main Spring Boot application and the Spring Boot Starter web dependency in build configuration file.

@SpringBootApplication

@EnableEurekaClient

@RestController

public class EurekaclientApplication {

   public static void main(String[] args) {

      SpringApplication.run(EurekaclientApplication.class, args);

   }

   @RequestMapping(value = "/")

   public String home() {

      return "Eureka Client application";

   }

}

 

Zuul Proxy Server and Routing

Zuul Server is a gateway application that handles all the requests and does the dynamic routing of microservice applications. The Zuul Server is also known as Edge Server.

<dependency>

   <groupId>org.springframework.cloud</groupId>

   <artifactId>spring-cloud-starter-zuul</artifactId>

</dependency>

 

@SpringBootApplication

@EnableZuulProxy

public class ZuulserverApplication {

   public static void main(String[] args) {

      SpringApplication.run(ZuulserverApplication.class, args);

   }

}

 

For Zuul routing, add the below properties in your application.properties file

 

spring.application.name = zuulserver

zuul.routes.products.path = /api/demo/**

zuul.routes.products.url = http://localhost:8080/

server.port = 8111

This means that http calls to /api/demo/ get forwarded to the products service.

Spring Cloud Configuration Server is a centralized application that manages all the application related configuration properties. 

<dependency>

   <groupId>org.springframework.cloud</groupId>

   <artifactId>spring-cloud-config-server</artifactId>

</dependency>

@SpringBootApplication

 

@EnableConfigServer

public class ConfigserverApplication {

   public static void main(String[] args) {

      SpringApplication.run(ConfigserverApplication.class, args);

   }

}

 

server.port = 8888

spring.cloud.config.server.native.searchLocations=file:///C:/configprop/

SPRING_PROFILES_ACTIVE=native

 

Configuration Server runs on the Tomcat port 8888 and application configuration properties are loaded from native search locations.

Now, in file:///C:/configprop/, place your client application - application.properties file.

The @RefreshScope annotation is used to load the configuration properties value from the Config server.

Spring Boot Actuator provides secured endpoints for monitoring and managing your Spring Boot application. By default, all actuator endpoints are secured.

<dependency>

   <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-starter-actuator</artifactId>

</dependency>

 

In the application.properties file, we need to disable the security for actuator endpoints.
management.security.enabled = false

 

/metrics

To view the application metrics such as memory used, memory free, threads, classes, system uptime etc.

/env

To view the list of Environment variables used in the application.

/beans

To view the Spring beans and its types, scopes and dependency.

/health

To view the application health

/info

To view the information about the Spring Boot application.

/trace

To view the list of Traces of your Rest endpoints.

 

Swagger

Swagger2 is an open source project used to generate the REST API documents for RESTful web services. It provides a user interface to access our RESTful web services via the web browser.

To enable the Swagger2 in Spring Boot application, you need to add the following dependencies in our build configurations file.

 

<dependency>

   <groupId>io.springfox</groupId>

   <artifactId>springfox-swagger2</artifactId>

   <version>2.7.0</version>

</dependency>

<dependency>

   <groupId>io.springfox</groupId>

   <artifactId>springfox-swagger-ui</artifactId>

   <version>2.7.0</version>

</dependency>

 

Now, add the @EnableSwagger2 annotation in your main Spring Boot application. The @EnableSwagger2 annotation is used to enable the Swagger2 for your Spring Boot application.

@SpringBootApplication

@EnableSwagger2

public class SwaggerDemoApplication {

   public static void main(String[] args) {

      SpringApplication.run(SwaggerDemoApplication.class, args);

   }

}

 

Next, create Docket Bean to configure Swagger2 for your Spring Boot application. We need to define the base package to configure REST API(s) for Swagger2.

@SpringBootApplication

@EnableSwagger2

public class SwaggerDemoApplication {

   public static void main(String[] args) {

      SpringApplication.run(SwaggerDemoApplication.class, args);

   }

   @Bean

   public Docket productApi() {

      return new Docket(DocumentationType.SWAGGER_2).select()

         .apis(RequestHandlerSelectors.basePackage("com.tutorialspoint.swaggerdemo")).build();

   }

}

 

Docker is a container management service that eases building and deployment.

First, create a file with the name Dockerfile under the directories src/main/docker with the contents shown below. Note that this file is important to create a Docker image.

FROM java:8

VOLUME /tmp

ADD dockerapp-0.0.1-SNAPSHOT.jar app.jar

RUN bash -c 'touch /app.jar'

ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

 

or Maven, add the Docker Maven plugin into your build configuration file pom.xml

<properties>

   <docker.image.prefix>spring-boot-tutorialspoint</docker.image.prefix>

</properties>

 

<build>

   <plugins>

      <plugin>

         <groupId>com.spotify</groupId>

         <artifactId>docker-maven-plugin</artifactId>

         <version>1.0.0</version>

        

         <configuration>

            <imageName>${docker.image.prefix}/${project.artifactId}</imageName>

            <dockerDirectory>src/main/docker</dockerDirectory>

            <resources>

               <resource>

                  <directory>${project.build.directory}</directory>

                  <include>${project.build.finalName}.jar</include>

               </resource>

            </resources>

         </configuration>

      </plugin>

     

      <plugin>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-maven-plugin</artifactId>

      </plugin>

   </plugins>

  

</build>

 

you can run your application by using the Maven command mvn package docker:build

 

See the Docker images by the command using docker images and see the image info on the console

docker images

Spring Boot - Tracing Micro Service Logs

Most developers face difficulty of tracing logs if any issue occurred. This can be solved by Spring Cloud Sleuth and ZipKin server for Spring Boot application.

 

Spring Cloud Sleuth

 

Spring cloud Sleuth logs are printed in the following format −

 

[application-name,traceid,spanid,zipkin-export]

Where,

·         Application-name = Name of the application

·         Traceid = each request and response traceid is same when calling same service or one service to another service.

·         Spanid = Span Id is printed along with Trace Id. Span Id is different every request and response calling one service to another service.

·         Zipkin-export = By default it is false. If it is true, logs will be exported to the Zipkin server.

Now, add the Spring Cloud Starter Sleuth dependency in your build configuration file as follows −

Maven users can add the following dependency in your pom.xml file –

 

<dependency>

   <groupId>org.springframework.cloud</groupId>

   <artifactId>spring-cloud-starter-sleuth</artifactId>

</dependency>

 

Now, add the application name in application.properties file

spring.application.name = tracinglogs

 

Zipkin Server

 

Zipkin is an application that monitors and manages the Spring Cloud Sleuth logs of your Spring Boot application. To build a Zipkin server, we need to add the Zipkin UI and Zipkin Server dependencies in our build configuration file.

Maven users can add the following dependency in your pom.xml file –

 

<dependency>

   <groupId>io.zipkin.java</groupId>

   <artifactId>zipkin-server</artifactId>

</dependency>

<dependency>

   <groupId>io.zipkin.java</groupId>

   <artifactId>zipkin-autoconfigure-ui</artifactId>

</dependency>

 

Add the @EnableZipkinServer annotation in your main Spring Boot application class fie. The @EnableZipkinServer annotation is used to enable your application act as a Zipkin server.

 

Then, add the following dependency in your client service application and point out the Zipkin Server URL to trace the microservice logs via Zipkin UI.

 

Now, add the Spring Cloud Starter Zipkin dependency in your build configuration file as shown −

Maven users can add the following dependency in pom.xml file –

 

<dependency>

   <groupId>org.springframework.cloud</groupId>

   <artifactId>spring-cloud-sleuth-zipkin</artifactId>

</dependency>

 

Now, add the Always Sampler Bean in your Spring Boot application to export the logs into Zipkin server.

 

@Bean

public AlwaysSampler defaultSampler() {

   return new AlwaysSampler();

}

 

If you add the AlwaysSampler Bean, then automatically Spring Sleuth Zipkin Export option will change from false to true.

Next, configure your Zipkin Server base URL in client service application.properties file.

spring.zipkin.baseUrl = http://localhost:9411/zipkin/

Then, provide the trace id and find the traces in Zipkin UI.

 

http://localhost:9411/zipkin/traces/{traceid}/

 

Spring Boot- Batch Service

Batch Service is a process to execute more than one command in a single task.

To create a Batch Service program, we need to add the Spring Boot Starter Batch dependency and HSQLDB dependency in our build configuration file.

 

Maven users can add the following dependencies in pom.xml file.

 

<dependency>

   <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-starter-batch</artifactId>

</dependency>

<dependency>

   <groupId>org.hsqldb</groupId>

   <artifactId>hsqldb</artifactId>

</dependency>

 

Now, add the simple CSV data file under classpath resources – src/main/resources and name the file as file.csv 

Next, write a SQL script for HSQLDB – under the classpath resource directory 

Create a POJO class for USERS model

Now, create an intermediate processor to do the operations after the reading the data from the CSV file and before writing the data into SQL.

 

package com.tutorialspoint.batchservicedemo;

 

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.batch.item.ItemProcessor;

 

public class UserItemProcessor implements ItemProcessor<User, User> {

   private static final Logger log = LoggerFactory.getLogger(UserItemProcessor.class);

 

   @Override

   public User process(final User user) throws Exception {

      final String firstName = user.getFirstName().toUpperCase();

      final String lastName = user.getLastName().toUpperCase();

      final User transformedPerson = new User(firstName, lastName);

 

      log.info("Converting (" + user + ") into (" + transformedPerson + ")");

      return transformedPerson;

   }

}

 

Let us create a Batch configuration file, to read the data from CSV and write into the SQL file as shown below. We need to add the @EnableBatchProcessing annotation in the configuration class file. The @EnableBatchProcessing annotation is used to enable the batch operations for your Spring Boot application.

 

 

@Configuration

@EnableBatchProcessing

public class BatchConfiguration {

   @Autowired

   public JobBuilderFactory jobBuilderFactory;

   @Autowired

   public StepBuilderFactory stepBuilderFactory;

   @Autowired

   public DataSource dataSource;

   @Bean

   public FlatFileItemReader<User> reader() {

      FlatFileItemReader<User> reader = new FlatFileItemReader<User>();

      reader.setResource(new ClassPathResource("file.csv"));

      reader.setLineMapper(new DefaultLineMapper<User>() {

         {

            setLineTokenizer(new DelimitedLineTokenizer() {

               {

                  setNames(new String[] { "firstName", "lastName" });

               }

            });

            setFieldSetMapper(new BeanWrapperFieldSetMapper<User>() {

               {

                  setTargetType(User.class);

               }

            });

         }

      });

      return reader;

   }

   @Bean

   public UserItemProcessor processor() {

      return new UserItemProcessor();

   }

   @Bean

   public JdbcBatchItemWriter<User> writer() {

      JdbcBatchItemWriter<User> writer = new JdbcBatchItemWriter<User>();

      writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<User>());

      writer.setSql("INSERT INTO USERS (first_name, last_name) VALUES (:firstName, :lastName)");

      writer.setDataSource(dataSource);

      return writer;

   }

   @Bean

   public Job importUserJob(JobCompletionNotificationListener listener) {

      return jobBuilderFactory.get("importUserJob").incrementer(

         new RunIdIncrementer()).listener(listener).flow(step1()).end().build();

   }

 

   @Bean

   public Step step1() {

      return stepBuilderFactory.get("step1").<User, User>chunk(10).reader(reader()).processor(processor()).writer(writer()).build();

   }

}

Next, we will have to write a Job Completion Notification Listener class – used to notify after the Job completion.

@Component

public class JobCompletionNotificationListener extends JobExecutionListenerSupport {

   private static final Logger log = LoggerFactory.getLogger(JobCompletionNotificationListener.class);

   private final JdbcTemplate jdbcTemplate;

 

   @Autowired

   public JobCompletionNotificationListener(JdbcTemplate jdbcTemplate) {

      this.jdbcTemplate = jdbcTemplate;

   }

   @Override

   public void afterJob(JobExecution jobExecution) {

      if (jobExecution.getStatus() == BatchStatus.COMPLETED) {

         log.info("!!! JOB FINISHED !! It's time to verify the results!!");

 

         List<User> results = jdbcTemplate.query(

            "SELECT first_name, last_name FROM USERS", new RowMapper<User>() {

           

            @Override

            public User mapRow(ResultSet rs, int row) throws SQLException {

               return new User(rs.getString(1), rs.getString(2));

            }

         });

 

         for (User person : results) {

            log.info("Found <" + person + "> in the database.");

         }

      }

   }

}

 

Spring Boot-Apache Kafka

Apache Kafka is an open source project used to publish and subscribe the messages based on the fault-tolerant messaging system. It is fast, scalable and distributed by design.

<dependency>

   <groupId>org.springframework.kafka</groupId>

   <artifactId>spring-kafka</artifactId>

   <version>2.1.0.RELEASE</version>

</dependency>

 

 

Producing Messages

To produce messages into Apache Kafka, we need to define the Configuration class for Producer configuration as shown –

 

@Configuration

public class KafkaProducerConfig {

   @Bean

   public ProducerFactory<String, String> producerFactory() {

      Map<String, Object> configProps = new HashMap<>();

      configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");

      configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);

      configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);

      return new DefaultKafkaProducerFactory<>(configProps);

   }

   @Bean

   public KafkaTemplate<String, String> kafkaTemplate() {

      return new KafkaTemplate<>(producerFactory());

   }

 

To publish a message, auto wire the Kafka Template object and produce the message as shown.

 

@Autowired

private KafkaTemplate<String, String> kafkaTemplate;

 

public void sendMessage(String msg) {

   kafkaTemplate.send(topicName, msg);

}  

 

Consuming a Message

To consume messages, we need to write a Consumer configuration class file 

@EnableKafka

@Configuration

public class KafkaConsumerConfig {

   @Bean

   public ConsumerFactory<String, String> consumerFactory() {

      Map<String, Object> props = new HashMap<>();

      props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:2181");

      props.put(ConsumerConfig.GROUP_ID_CONFIG, "group-id");

      props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);

      props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);

      return new DefaultKafkaConsumerFactory<>(props);

   }

   @Bean

   public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() {

      ConcurrentKafkaListenerContainerFactory<String, String>

      factory = new ConcurrentKafkaListenerContainerFactory<>();

      factory.setConsumerFactory(consumerFactory());

      return factory;

   }

}     

 

Next, write a Listener to listen to the messages.

 

@KafkaListener(topics = "tutorialspoint", groupId = "group-id")

public void listen(String message) {

   System.out.println("Received Messasge in group - group-id: " + message);

}

 

Let us call the sendMessage() method from ApplicationRunner class run method from the main Spring Boot application class file and consume the message from the same class file.

 

@SpringBootApplication

public class KafkaDemoApplication implements ApplicationRunner {

   @Autowired

   private KafkaTemplate<String, String> kafkaTemplate;

 

   public void sendMessage(String msg) {

      kafkaTemplate.send("tutorialspoint", msg);

   }

   public static void main(String[] args) {

      SpringApplication.run(KafkaDemoApplication.class, args);

   }

   @KafkaListener(topics = "tutorialspoint", groupId = "group-id")

   public void listen(String message) {

      System.out.println("Received Messasge in group - group-id: " + message);

   }

   @Override

   public void run(ApplicationArguments args) throws Exception {

      sendMessage("Hi Welcome to Spring For Apache Kafka");

   }

}

 

Spring Boot- Twilio

Twilio is a 3rd party application used to send SMS and make voice calls from our application. It allows us to send the SMS and make voice calls programmatically.

<dependency>

   <groupId>com.twilio.sdk</groupId>

   <artifactId>twilio</artifactId>

   <version>7.16.1</version>

</dependency>

 

Now, initialize the Twilio account with ACCOUNT_SID and AUTH_ID in static block as shown −

 

static {

   Twilio.init(ACCOUNT_SID, AUTH_ID);

}

 

Sending SMS

 

To send the SMS, we need to provide a from-number and to-number to the Message.create() method. Message body content also we need to provide for the method Message.creator()as shown –

 

Message.creator(new PhoneNumber("to-number"), new PhoneNumber("from-number"),

   "Message from Spring Boot Application").create();

 

Voice Calls

 

To make voice calls by using Twilio, we need to call the Call.creator() method. For this method, we need to provide a to-number, from-number, and voice-note as shown here.

 

Call.creator(new PhoneNumber("<to-number>"), new PhoneNumber("<from-number>"),

   new URI("http://demo.twilio.com/docs/voice.xml")).create();

 

Spring Boot – Unit Test Case

Unit Testing is a one of the testing done by the developers to make sure individual unit or component functionalities are working fine.

Mockito

For injecting Mockito Mocks into Spring Beans, we need to add the Mockito-core dependency in our build configuration file.

 

Maven users can add the following dependency in your pom.xml file.

<dependency>

   <groupId>org.mockito</groupId>

   <artifactId>mockito-core</artifactId>

   <version>2.13.0</version>

</dependency>

<dependency>

   <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-starter-test</artifactId>

   <scope>test</scope>

</dependency>

 

Then, configure the Application context for the tests. The @Profile(“test”) annotation is used to configure the class when the Test cases are running.

package com.tutorialspoint.mockitodemo;

 

import org.mockito.Mockito;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.Primary;

import org.springframework.context.annotation.Profile;

 

@Profile("test")

@Configuration

public class ProductServiceTestConfiguration {

   @Bean

   @Primary

   public ProductService productService() {

      return Mockito.mock(ProductService.class);

   }

 

Now, you can write a Unit Test case like below

@SpringBootTest

@ActiveProfiles("test")

@RunWith(SpringJUnit4ClassRunner.class)

public class MockitoDemoApplicationTests {

   @Autowired

   private OrderService orderService;

  

   @Autowired

   private ProductService productService;

 

   @Test

   public void whenUserIdIsProvided_thenRetrievedNameIsCorrect() {

      Mockito.when(productService.getProductName()).thenReturn("Mock Product Name");

      String testName = orderService.getProductName();

      Assert.assertEquals("Mock Product Name", testName);

   }

}

 

Writing a Unit Test for REST Controller

 

First, we need to create Abstract class file used to create web application context by using MockMvc and define the mapToJson() and mapFromJson() methods to convert the Java object into JSON string and convert the JSON string into Java object.

 

@RunWith(SpringJUnit4ClassRunner.class)

@SpringBootTest(classes = DemoApplication.class)

@WebAppConfiguration

public abstract class AbstractTest {

   protected MockMvc mvc;

   @Autowired

   WebApplicationContext webApplicationContext;

 

   protected void setUp() {

      mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();

   }

   protected String mapToJson(Object obj) throws JsonProcessingException {

      ObjectMapper objectMapper = new ObjectMapper();

      return objectMapper.writeValueAsString(obj);

   }

   protected <T> T mapFromJson(String json, Class<T> clazz)

      throws JsonParseException, JsonMappingException, IOException {

     

      ObjectMapper objectMapper = new ObjectMapper();

      return objectMapper.readValue(json, clazz);

   }

}

 

Next, write a class file that extends the AbstractTest class and write a Unit Test for each method such GET, POST, PUT and DELETE.

@Test

public void getProductsList() throws Exception {

   String uri = "/products";

   MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(uri)

      .accept(MediaType.APPLICATION_JSON_VALUE)).andReturn();

  

   int status = mvcResult.getResponse().getStatus();

   assertEquals(200, status);

   String content = mvcResult.getResponse().getContentAsString();

   Product[] productlist = super.mapFromJson(content, Product[].class);

   assertTrue(productlist.length > 0);

}

 

The code for POST API test case is given below. This API is to create a product.

@Test

public void createProduct() throws Exception {

   String uri = "/products";

   Product product = new Product();

   product.setId("3");

   product.setName("Ginger");

  

   String inputJson = super.mapToJson(product);

   MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.post(uri)

      .contentType(MediaType.APPLICATION_JSON_VALUE).content(inputJson)).andReturn();

  

   int status = mvcResult.getResponse().getStatus();

   assertEquals(201, status);

   String content = mvcResult.getResponse().getContentAsString();

   assertEquals(content, "Product is created successfully");

}

The code for PUT API Test case is given below. This API is to update the existing product.

@Test

public void updateProduct() throws Exception {

   String uri = "/products/2";

   Product product = new Product();

   product.setName("Lemon");

  

   String inputJson = super.mapToJson(product);

   MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.put(uri)

      .contentType(MediaType.APPLICATION_JSON_VALUE).content(inputJson)).andReturn();

  

   int status = mvcResult.getResponse().getStatus();

   assertEquals(200, status);

   String content = mvcResult.getResponse().getContentAsString();

   assertEquals(content, "Product is updated successsfully");

}

The code for Delete API Test case is given below. This API will delete the existing product.

@Test

public void deleteProduct() throws Exception {

   String uri = "/products/2";

   MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.delete(uri)).andReturn();

   int status = mvcResult.getResponse().getStatus();

   assertEquals(200, status);

   String content = mvcResult.getResponse().getContentAsString();

   assertEquals(content, "Product is deleted successsfully");

}

 

JDBCTemplate

 

To access the Relational Database by using JdbcTemplate in Spring Boot application, we need to add the Spring Boot Starter JDBC dependency in our build configuration file.

Then, if you @Autowired the JdbcTemplate class, Spring Boot automatically connects the Database and sets the Datasource for the JdbcTemplate object.

 

@Autowired

JdbcTemplate jdbcTemplate;

Collection<Map<String, Object>> rows = jdbc.queryForList("SELECT QUERY");

 

The @Repository annotation should be added into the class file. The @Repository annotation is used to create database repository for your Spring Boot application.

 

@Repository

public class ProductServiceDAO {

}

 

Now, create a Configuration class to create a DataSource and JdbcTemplate for multiple data sources.

import javax.sql.DataSource;

 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.Primary;

import org.springframework.jdbc.core.JdbcTemplate;

 

@Configuration

public class DatabaseConfig {

   @Bean(name = "dbProductService")

   @ConfigurationProperties(prefix = "spring.dbProductService")

   @Primary

   public DataSource createProductServiceDataSource() {

      return DataSourceBuilder.create().build();

   }

   @Bean(name = "dbUserService")

   @ConfigurationProperties(prefix = "spring.dbUserService")

   public DataSource createUserServiceDataSource() {

      return DataSourceBuilder.create().build();

   }

   @Bean(name = "jdbcProductService")

   @Autowired

   public JdbcTemplate createJdbcTemplate_ProductService(@Qualifier("dbProductService") DataSource productServiceDS) {

      return new JdbcTemplate(productServiceDS);

   }

   @Bean(name = "jdbcUserService")

   @Autowired

   public JdbcTemplate createJdbcTemplate_UserService(@Qualifier("dbUserService") DataSource userServiceDS) {

      return new JdbcTemplate(userServiceDS);

   }

}

Then, auto wire the JDBCTemplate object by using @Qualifier annotation.

@Qualifier("jdbcProductService")

@Autowired

JdbcTemplate jdbcTemplate;

@Qualifier("jdbcUserService")

@Autowired

JdbcTemplate jdbcTemplate;

 

Spring Boot-Security

 

<dependencies>

      <dependency>

         <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-starter-jdbc</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.security.oauth</groupId>

         <artifactId>spring-security-oauth2</artifactId>

      </dependency>

      <dependency>

         <groupId>org.springframework.security</groupId>

         <artifactId>spring-security-jwt</artifactId>

      </dependency>

Now, in the main Spring Boot application, add the @EnableAuthorizationServer and @EnableResourceServer annotation to act as an Auth server and Resource Server in the same application.

 

@SpringBootApplication

@EnableAuthorizationServer

@EnableResourceServer

@RestController

public class WebsecurityappApplication {

   public static void main(String[] args) {

      SpringApplication.run(WebsecurityappApplication.class, args);

   }

   @RequestMapping(value = "/products")

   public String getProductName() {

      return "Honey";  

   }

}

 

Use the following code to define the POJO class to store the User information for authentication.

package com.tutorialspoint.websecurityapp;

 

import java.util.ArrayList;

import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;

 

public class UserEntity {

   private String username;

   private String password;

   private Collection<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>();

  

   public String getPassword() {

      return password;

   }

   public void setPassword(String password) {

      this.password = password;

   }

   public Collection<GrantedAuthority> getGrantedAuthoritiesList() {

      return grantedAuthoritiesList;

   }

   public void setGrantedAuthoritiesList(Collection<GrantedAuthority> grantedAuthoritiesList) {

      this.grantedAuthoritiesList = grantedAuthoritiesList;

   }

   public String getUsername() {

      return username;

   }

   public void setUsername(String username) {

      this.username = username;

   }

}

 

Now, use the following code and define the CustomUser class that extends the org.springframework.security.core.userdetails.User class for Spring Boot authentication.

package com.tutorialspoint.websecurityapp;

 

import org.springframework.security.core.userdetails.User;

 

public class CustomUser extends User {

   private static final long serialVersionUID = 1L;

   public CustomUser(UserEntity user) {

      super(user.getUsername(), user.getPassword(), user.getGrantedAuthoritiesList());

   }

}

 

You can create the @Repository class to read the User information from the database and send it to the Custom user service and also add the granted authority “ROLE_SYSTEMADMIN”.

 

@Repository

public class OAuthDao {

   @Autowired

   private JdbcTemplate jdbcTemplate;

 

   public UserEntity getUserDetails(String username) {

      Collection<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>();

      String userSQLQuery = "SELECT * FROM USERS WHERE USERNAME=?";

      List<UserEntity> list = jdbcTemplate.query(userSQLQuery, new String[] { username },

         (ResultSet rs, int rowNum) -> {

        

         UserEntity user = new UserEntity();

         user.setUsername(username);

         user.setPassword(rs.getString("PASSWORD"));

         return user;

      });

      if (list.size() > 0) {

         GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_SYSTEMADMIN");

         grantedAuthoritiesList.add(grantedAuthority);

         list.get(0).setGrantedAuthoritiesList(grantedAuthoritiesList);

         return list.get(0);

      }

      return null;

   }

}

 

You can create a Custom User detail service class that extends the org.springframework.security.core.userdetails.UserDetailsService to call the DAO repository class as shown.

package com.tutorialspoint.websecurityapp;

 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.security.core.userdetails.UserDetailsService;

import org.springframework.security.core.userdetails.UsernameNotFoundException;

import org.springframework.stereotype.Service;

 

@Service

public class CustomDetailsService implements UserDetailsService {

   @Autowired

   OAuthDao oauthDao;

 

   @Override

   public CustomUser loadUserByUsername(final String username) throws UsernameNotFoundException {

      UserEntity userEntity = null;

      try {

         userEntity = oauthDao.getUserDetails(username);

         CustomUser customUser = new CustomUser(userEntity);

         return customUser;

      } catch (Exception e) {

         e.printStackTrace();

         throw new UsernameNotFoundException("User " + username + " was not found in the database");

      }

   }

}

 

Next, create a @configuration class to enable the Web Security, defining the Password encoder (BCryptPasswordEncoder), and defining the AuthenticationManager bean. The Security configuration class should extend WebSecurityConfigurerAdapter class.

 

@Configuration

@EnableWebSecurity

@EnableGlobalMethodSecurity(prePostEnabled = true)

public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

   @Autowired

   private CustomDetailsService customDetailsService;

 

   @Bean

   public PasswordEncoder encoder() {

      return new BCryptPasswordEncoder();

   }

   @Override

   @Autowired

   protected void configure(AuthenticationManagerBuilder auth) throws Exception {

      auth.userDetailsService(customDetailsService).passwordEncoder(encoder());

   }

   @Override

   protected void configure(HttpSecurity http) throws Exception {

      http.authorizeRequests().anyRequest().authenticated().and().sessionManagement()

         .sessionCreationPolicy(SessionCreationPolicy.NEVER);

   }

   @Override

   public void configure(WebSecurity web) throws Exception {

      web.ignoring();

   }

   @Override

   @Bean

   public AuthenticationManager authenticationManagerBean() throws Exception {

      return super.authenticationManagerBean();

   }

}

 

 

Spring Boot - Google OAuth2

 

First, add the Spring Boot OAuth2 security dependency in your build configuration file and your build configuration file 

<dependency>

 <groupId>org.springframework.security.oauth</groupId>

 <artifactId>spring-security-oauth2</artifactId>

</dependency>

Now, add the HTTP Endpoint to read the User Principal from the Google after authenticating via Spring Boot in main Spring Boot application class file as given below –

 

@SpringBootApplication

@RestController

public class GoogleserviceApplication {

   public static void main(String[] args) {

      SpringApplication.run(GoogleserviceApplication.class, args);

   }

   @RequestMapping(value = "/user")

   public Principal user(Principal principal) {

      return principal;

   }

}

Now, write a Configuration file to enable the OAuth2SSO for web security and remove the authentication for index.html file

@Configuration

@EnableOAuth2Sso

public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

   @Override

   protected void configure(HttpSecurity http) throws Exception {

      http

         .csrf()

         .disable()

         .antMatcher("/**")

         .authorizeRequests()

         .antMatchers("/", "/index.html")

         .permitAll()

         .anyRequest()

         .authenticated();

   }

}

Next, add the index.html file under static resources and add the link to redirect into user HTTP Endpoint to read the Google user Principal as shown below −

<!DOCTYPE html>

<html>

   <head>

      <meta charset = "ISO-8859-1">

      <title>Insert title here</title>

   </head>

   <body>

      <a href = "user">Click here to Google Login</a>

   </body>

</html>

 

Then, go the Credentials section and create a credentials and choose OAuth Client ID.

Next, provide a Product Name in OAuth2 consent screen.

Next, choose the Application Type as “Web application”, provide the Authorized JavaScript origins and Authorized redirect URIs.

Now, your OAuth2 Client Id and Client Secret is created.

Next, add the Client Id and Client Secret in your application properties file.

 

security.oauth2.client.clientId = <CLIENT_ID>

security.oauth2.client.clientSecret = <CLIENT_SECRET>

security.oauth2.client.accessTokenUri  =  https://www.googleapis.com/oauth2/v3/token

security.oauth2.client.userAuthorizationUri  =  https://accounts.google.com/o/oauth2/auth

security.oauth2.client.tokenName = oauth_token

security.oauth2.client.authenticationScheme = query

security.oauth2.client.clientAuthenticationScheme = form

security.oauth2.client.scope = profile email

 

security.oauth2.resource.userInfoUri  =  https://www.googleapis.com/userinfo/v2/me

security.oauth2.resource.preferTokenInfo = false

 

Comments

Popular posts from this blog

PUTTY - The server's host key is not cached in the registry cache

OIM-12c Installation - FMW - SOA - IDM

SAML & OAuth 2.0