Welcome To Fusebes - Dev & Programming Blog

Intro to Spring Boot Starters
26
Mar
2021

Intro to Spring Boot Starters

1. Overview

Dependency management is a critical aspects of any complex project. And doing this manually is less than ideal; the more time you spent on it the less time you have on the other important aspects of the project.

Spring Boot starters were built to address exactly this problem. Starter POMs are a set of convenient dependency descriptors that you can include in your application. You get a one-stop-shop for all the Spring and related technology that you need, without having to hunt through sample code and copy-paste loads of dependency descriptors.

We have more than 30 Boot starters available – let’s see some of them in the following section

2. The Web Starter

First, let’s look at developing the REST service; we can use libraries like Spring MVC, Tomcat and Jackson – a lot of dependencies for a single application.

Spring Boot starters can help to reduce the number of manually added dependencies just by adding one dependency. So instead of manually specifying the dependencies just add one starter as in the following example:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Now we can create a REST controller. For the sake of simplicity we won’t use the database and focus on the REST controller:

@RestController
public class GenericEntityController {
    private List<GenericEntity> entityList = new ArrayList<>();

    @RequestMapping("/entity/all")
    public List<GenericEntity> findAll() {
        return entityList;
    }

    @RequestMapping(value = "/entity", method = RequestMethod.POST)
    public GenericEntity addEntity(GenericEntity entity) {
        entityList.add(entity);
        return entity;
    }

    @RequestMapping("/entity/findby/{id}")
    public GenericEntity findById(@PathVariable Long id) {
        return entityList.stream().
                 filter(entity -> entity.getId().equals(id)).
                   findFirst().get();
    }
}

The GenericEntity is a simple bean with id of type Long and value of type String.

That’s it – with the application running, you can access http://localhost:8080/entity/all and check the controller is working.

We have created a REST application with quite a minimal configuration.

3. The Test Starter

For testing we usually use the following set of libraries: Spring Test, JUnit, Hamcrest, and Mockito. We can include all of these libraries manually, but Spring Boot starter can be used to automatically include these libraries in the following way:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

Notice that you don’t need to specify the version number of an artifact. Spring Boot will figure out what version to use – all you need to specify is the version of spring-boot-starter-parent artifact. If later on you need to upgrade the Boot library and dependencies, just upgrade the Boot version in one place and it will take care of the rest.

Let’s actually test the controller we created in the previous example.

There are two ways to test the controller:

  • Using the mock environment
  • Using the embedded Servlet container (like Tomcat or Jetty)

In this example we’ll use a mock environment:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class SpringBootApplicationIntegrationTest {
    @Autowired
    private WebApplicationContext webApplicationContext;
    private MockMvc mockMvc;

    @Before
    public void setupMockMvc() {
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

    @Test
    public void givenRequestHasBeenMade_whenMeetsAllOfGivenConditions_thenCorrect()
      throws Exception { 
        MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
        MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
        mockMvc.perform(MockMvcRequestBuilders.get("/entity/all")).
        andExpect(MockMvcResultMatchers.status().isOk()).
        andExpect(MockMvcResultMatchers.content().contentType(contentType)).
        andExpect(jsonPath("$", hasSize(4))); 
    } 
}

The above test calls the /entity/all endpoint and verifies that the JSON response contains 4 elements. For this test to pass, we also have to initialize our list in the controller class:

public class GenericEntityController {
    private List<GenericEntity> entityList = new ArrayList<>();

    {
        entityList.add(new GenericEntity(1l, "entity_1"));
        entityList.add(new GenericEntity(2l, "entity_2"));
        entityList.add(new GenericEntity(3l, "entity_3"));
        entityList.add(new GenericEntity(4l, "entity_4"));
    }
    //...
}

What is important here is that @WebAppConfiguration annotation and MockMVC are part of the spring-test module, hasSize is a Hamcrest matcher, and @Before is a JUnit annotation. These are all available by importing one this one starter dependency.

4. The Data JPA Starter

Most web applications have some sort of persistence – and that’s quite often JPA.

Instead of defining all of the associated dependencies manually – let’s go with the starter instead:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

Notice that out of the box we have automatic support for at least the following databases: H2, Derby and Hsqldb. In our example, we’ll use H2.

Now let’s create the repository for our entity:

public interface GenericEntityRepository extends JpaRepository<GenericEntity, Long> {}

Time to test the code. Here is the JUnit test:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
public class SpringBootJPATest {
    
    @Autowired
    private GenericEntityRepository genericEntityRepository;

    @Test
    public void givenGenericEntityRepository_whenSaveAndRetreiveEntity_thenOK() {
        GenericEntity genericEntity = 
          genericEntityRepository.save(new GenericEntity("test"));
        GenericEntity foundedEntity = 
          genericEntityRepository.findOne(genericEntity.getId());
        
        assertNotNull(foundedEntity);
        assertEquals(genericEntity.getValue(), foundedEntity.getValue());
    }
}

We didn’t spend time on specifying the database vendor, URL connection, and credentials. No extra configuration is necessary as we’re benefiting from the solid Boot defaults; but of course all of these details can still be configured if necessary.

5. The Mail Starter

A very common task in enterprise development is sending email, and dealing directly with Java Mail API usually can be difficult.

Spring Boot starter hides this complexity – mail dependencies can be specified in the following way:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

Now we can directly use the JavaMailSender, so let’s write some tests.

For the testing purpose, we need a simple SMTP server. In this example, we’ll use Wiser. This is how we can include it in our POM:

<dependency>
    <groupId>org.subethamail</groupId>
    <artifactId>subethasmtp</artifactId>
    <version>3.1.7</version>
    <scope>test</scope>
</dependency>

Here is the source code for the test:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
public class SpringBootMailTest {
    @Autowired
    private JavaMailSender javaMailSender;

    private Wiser wiser;

    private String userTo = "user2@localhost";
    private String userFrom = "user1@localhost";
    private String subject = "Test subject";
    private String textMail = "Text subject mail";

    @Before
    public void setUp() throws Exception {
        final int TEST_PORT = 25;
        wiser = new Wiser(TEST_PORT);
        wiser.start();
    }

    @After
    public void tearDown() throws Exception {
        wiser.stop();
    }

    @Test
    public void givenMail_whenSendAndReceived_thenCorrect() throws Exception {
        SimpleMailMessage message = composeEmailMessage();
        javaMailSender.send(message);
        List<WiserMessage> messages = wiser.getMessages();

        assertThat(messages, hasSize(1));
        WiserMessage wiserMessage = messages.get(0);
        assertEquals(userFrom, wiserMessage.getEnvelopeSender());
        assertEquals(userTo, wiserMessage.getEnvelopeReceiver());
        assertEquals(subject, getSubject(wiserMessage));
        assertEquals(textMail, getMessage(wiserMessage));
    }

    private String getMessage(WiserMessage wiserMessage)
      throws MessagingException, IOException {
        return wiserMessage.getMimeMessage().getContent().toString().trim();
    }

    private String getSubject(WiserMessage wiserMessage) throws MessagingException {
        return wiserMessage.getMimeMessage().getSubject();
    }

    private SimpleMailMessage composeEmailMessage() {
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setTo(userTo);
        mailMessage.setReplyTo(userFrom);
        mailMessage.setFrom(userFrom);
        mailMessage.setSubject(subject);
        mailMessage.setText(textMail);
        return mailMessage;
    }
}

In the test, the @Before and @After methods are in charge of starting and stopping the mail server.

Notice that we’re wiring in the JavaMailSender bean – the bean was automatically created by Spring Boot.

Just like any other defaults in Boot, the email settings for the JavaMailSender can be customized in application.properties:

spring.mail.host=localhost
spring.mail.port=25
spring.mail.properties.mail.smtp.auth=false

So we configured the mail server on localhost:25 and we didn’t require authentication.

6. Conclusion

In this article we have given an overview of Starters, explained why we need them and provided examples on how to use them in your projects.

Let’s recap the benefits of using Spring Boot starters:

  • increase pom manageability
  • production-ready, tested & supported dependency configurations
  • decrease the overall configuration time for the project
Java Add/subtract years, months, days, hours, minutes, or seconds to a Date & Time
03
Mar
2021

Java Add/subtract years, months, days, hours, minutes, or seconds to a Date & Time

In this article, you’ll find several ways of adding or subtracting years, months, days, hours, minutes, or seconds to a Date in Java.

Add/Subtract years, months, days, hours, minutes, seconds to a Date using Calendar class

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class CalendarAddSubtractExample {
    public static void main(String[] args) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = new Date();
        System.out.println("Current Date " + dateFormat.format(date));

        // Convert Date to Calendar
        Calendar c = Calendar.getInstance();
        c.setTime(date);

        // Perform addition/subtraction
        c.add(Calendar.YEAR, 2);
        c.add(Calendar.MONTH, 1);
        c.add(Calendar.DATE, -10);
        c.add(Calendar.HOUR, -4);
        c.add(Calendar.MINUTE, 30);
        c.add(Calendar.SECOND, 50);

        // Convert calendar back to Date
        Date currentDatePlusOne = c.getTime();

        System.out.println("Updated Date " + dateFormat.format(currentDatePlusOne));
    }
}
# Output
Current Date 2020-02-24 19:35:00
Updated Date 2022-03-14 16:05:50

Add/Subtract years, months, weeks, days, hours, minutes, seconds to LocalDateTime

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

public class LocalDateTimeAddSubtractExample {
    public static void main(String[] args) {
        LocalDateTime dateTime = LocalDateTime.of(2020, 1, 26, 10, 30, 45);

        // Add years, months, weeks, days, hours, minutes, seconds to LocalDateTime
        LocalDateTime newDateTime = dateTime.plusYears(3).plusMonths(1).plusWeeks(4).plusDays(2).plusHours(2);
        System.out.println(newDateTime);


        // Subtract years, months, weeks, days, hours, minutes, seconds to LocalDateTime
        newDateTime = dateTime.minusYears(3).minusMonths(1).minusWeeks(4).minusDays(2).minusMinutes(30);
        System.out.println(newDateTime);

        // Add/Subtract using the generic plus/minus method
        System.out.println(dateTime.plus(2, ChronoUnit.DAYS));

    }
}
# Output
2023-03-28T12:30:45
2016-11-26T10:00:45
2020-01-28T10:30:45

Add/Subtract days, weeks, months, years to LocalDate

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class LocalDateAddSubtractExample {
    public static void main(String[] args) {
        LocalDate date = LocalDate.of(2020, 1, 26);

        // Add years, months, weeks, days to LocalDate
        LocalDate newDate = date.plusYears(3).plusMonths(1).plusWeeks(4).plusDays(2);
        System.out.println(newDate);


        // Subtract years, months, weeks, days to LocalDate
        newDate = date.minusYears(3).minusMonths(1).minusWeeks(4).minusDays(2);
        System.out.println(newDate);

        // Add/Subtract using the generic plus/minus method
        System.out.println(date.plus(2, ChronoUnit.DAYS));

    }
}
2023-03-28
2016-11-26
2020-01-28

Add/Subtract hours, minutes, seconds to LocalTime

import java.time.LocalTime;
import java.time.temporal.ChronoUnit;

public class LocalTimeAddSubtractExample {
    public static void main(String[] args) {
        LocalTime time = LocalTime.of(11, 45, 20);

        // Add hours, minutes, or seconds
        LocalTime newTime = time.plusHours(2).plusMinutes(30).plusSeconds(80);
        System.out.println(newTime);

        // Subtract hours, minutes, or seconds
        LocalTime updatedTime = time.minusHours(2).minusMinutes(30).minusSeconds(30);
        System.out.println(updatedTime);

        // Add/Subtract using the generic plus/minus method
        System.out.println(time.plus(1, ChronoUnit.HOURS));

    }
}
# Output
14:16:40
09:14:50
12:45:20
Getting started with Spring Boot
26
Mar
2021

Getting started with Spring Boot

1. Overview

Spring Boot is an opinionated, convention-over-configuration focused addition to the Spring platform – highly useful to get started with minimum effort and create stand-alone, production-grade applications.

This tutorial is a starting point for Boot – a way to get started in a simple manner, with a basic web application.

We’ll go over some core configuration, a front-end, quick data manipulation, and exception handling.

2. Setup

First, let’s use Spring Initializr to generate the base for our project.

The generated project relies on the Boot parent:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.0</version>
    <relativePath />
</parent>

The initial dependencies are going to be quite simple:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>

3. Application Configuration

Next, we’ll configure a simple main class for our application:

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

Notice how we’re using @SpringBootApplication as our primary application configuration class; behind the scenes, that’s equivalent to @Configuration@EnableAutoConfiguration, and @ComponentScan together.

Finally, we’ll define a simple application.properties file – which for now only has one property:

server.port=8081

server.port changes the server port from the default 8080 to 8081; there are of course many more Spring Boot properties available.

4. Simple MVC View

Let’s now add a simple front end using Thymeleaf.

First, we need to add the spring-boot-starter-thymeleaf dependency to our pom.xml:

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-thymeleaf</artifactId> 
</dependency>

That enables Thymeleaf by default – no extra configuration is necessary.

We can now configure it in our application.properties:

spring.thymeleaf.cache=false
spring.thymeleaf.enabled=true 
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

spring.application.name=Bootstrap Spring Boot

Next, we’ll define a simple controller and a basic home page – with a welcome message:

@Controller
public class SimpleController {
    @Value("${spring.application.name}")
    String appName;

    @GetMapping("/")
    public String homePage(Model model) {
        model.addAttribute("appName", appName);
        return "home";
    }
}

Finally, here is our home.html:

<html>
<head><title>Home Page</title></head>
<body>
<h1>Hello !</h1>
<p>Welcome to <span th:text="${appName}">Our App</span></p>
</body>
</html>

Note how we used a property we defined in our properties – and then injected that so that we can show it on our home page.

5. Security

Next, let’s add security to our application – by first including the security starter:

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-security</artifactId> 
</dependency>

By now, you’re hopefully noticing a pattern – most Spring libraries are easily imported into our project with the use of simple Boot starters.

Once the spring-boot-starter-security dependency on the classpath of the application – all endpoints are secured by default, using either httpBasic or formLogin based on Spring Security’s content-negotiation strategy.

That’s why, if we have the starter on the classpath, we should usually define our own custom Security configuration by extending the WebSecurityConfigurerAdapter class:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .anyRequest()
            .permitAll()
            .and().csrf().disable();
    }
}

In our example, we’re allowing unrestricted access to all endpoints.

Of course, Spring Security is an extensive topic and one not easily covered in a couple of lines of configuration – so I definitely encourage you to go deeper into the topic.

6. Simple Persistence

Let’s start by defining our data model – a simple Book entity:

@Entity
public class Book {
 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column(nullable = false, unique = true)
    private String title;

    @Column(nullable = false)
    private String author;
}

And its repository, making good use of Spring Data here:

public interface BookRepository extends CrudRepository<Book, Long> {
    List<Book> findByTitle(String title);
}

Finally, we need to of course configure our new persistence layer:

@EnableJpaRepositories("com.baeldung.persistence.repo") 
@EntityScan("com.baeldung.persistence.model")
@SpringBootApplication 
public class Application {
   ...
}

Note that we’re using:

  • @EnableJpaRepositories to scan the specified package for repositories
  • @EntityScan to pick up our JPA entities

To keep things simple, we’re using an H2 in-memory database here – so that we don’t have any external dependencies when we run the project.

Once we include H2 dependency, Spring Boot auto-detects it and sets up our persistence with no need for extra configuration, other than the data source properties:

spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:bootapp;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=

Of course, like security, persistence is a broader topic than this basic set here, and one you should certainly explore further.

7. Web and the Controller

Next, let’s have a look at a web tier – and we’ll start that by setting up a simple controller – the BookController.

We’ll implement basic CRUD operations exposing Book resources with some simple validation:

@RestController
@RequestMapping("/api/books")
public class BookController {

    @Autowired
    private BookRepository bookRepository;

    @GetMapping
    public Iterable findAll() {
        return bookRepository.findAll();
    }

    @GetMapping("/title/{bookTitle}")
    public List findByTitle(@PathVariable String bookTitle) {
        return bookRepository.findByTitle(bookTitle);
    }

    @GetMapping("/{id}")
    public Book findOne(@PathVariable Long id) {
        return bookRepository.findById(id)
          .orElseThrow(BookNotFoundException::new);
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Book create(@RequestBody Book book) {
        return bookRepository.save(book);
    }

    @DeleteMapping("/{id}")
    public void delete(@PathVariable Long id) {
        bookRepository.findById(id)
          .orElseThrow(BookNotFoundException::new);
        bookRepository.deleteById(id);
    }

    @PutMapping("/{id}")
    public Book updateBook(@RequestBody Book book, @PathVariable Long id) {
        if (book.getId() != id) {
          throw new BookIdMismatchException();
        }
        bookRepository.findById(id)
          .orElseThrow(BookNotFoundException::new);
        return bookRepository.save(book);
    }
}

Given this aspect of the application is an API, we made use of the @RestController annotation here – which equivalent to a @Controller along with @ResponseBody – so that each method marshalls the returned resource right to the HTTP response.

Just one note worth pointing out – we’re exposing our Book entity as our external resource here. That’s fine for our simple application here, but in a real-world application, you will likely want to separate these two concepts.

8. Error Handling

Now that the core application is ready to go, let’s focus on a simple centralized error handling mechanism using @ControllerAdvice:

@ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler({ BookNotFoundException.class })
    protected ResponseEntity<Object> handleNotFound(
      Exception ex, WebRequest request) {
        return handleExceptionInternal(ex, "Book not found", 
          new HttpHeaders(), HttpStatus.NOT_FOUND, request);
    }

    @ExceptionHandler({ BookIdMismatchException.class, 
      ConstraintViolationException.class, 
      DataIntegrityViolationException.class })
    public ResponseEntity<Object> handleBadRequest(
      Exception ex, WebRequest request) {
        return handleExceptionInternal(ex, ex.getLocalizedMessage(), 
          new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
    }
}

Beyond the standard exceptions we’re handling here, we’re also using a custom exception:

BookNotFoundException:

public class BookNotFoundException extends RuntimeException {

    public BookNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
    // ...
}

This should give you an idea of what’s possible with this global exception handling mechanism. If you’d like to see a full implementation, have a look at the in-depth tutorial.

Note that Spring Boot also provides an /error mapping by default. We can customize its view by creating a simple error.html:

<html lang="en">
<head><title>Error Occurred</title></head>
<body>
    <h1>Error Occurred!</h1>    
    <b>[<span th:text="${status}">status</span>]
        <span th:text="${error}">error</span>
    </b>
    <p th:text="${message}">message</p>
</body>
</html>

Like most other aspects in Boot, we can control that with a simple property:

server.error.path=/error2

9. Testing

Finally, let’s test our new Books API.

We can make use of @SpringBootTest to load the application context and verify there are no errors when running the app:

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringContextTest {

    @Test
    public void contextLoads() {
    }
}

Next, let’s add a JUnit test that verifies the calls to the API we’re written, using RestAssured:

public class SpringBootBootstrapLiveTest {

    private static final String API_ROOT
      = "http://localhost:8081/api/books";

    private Book createRandomBook() {
        Book book = new Book();
        book.setTitle(randomAlphabetic(10));
        book.setAuthor(randomAlphabetic(15));
        return book;
    }

    private String createBookAsUri(Book book) {
        Response response = RestAssured.given()
          .contentType(MediaType.APPLICATION_JSON_VALUE)
          .body(book)
          .post(API_ROOT);
        return API_ROOT + "/" + response.jsonPath().get("id");
    }
}

First, we can try to find books using variant methods:

@Test
public void whenGetAllBooks_thenOK() {
    Response response = RestAssured.get(API_ROOT);
 
    assertEquals(HttpStatus.OK.value(), response.getStatusCode());
}

@Test
public void whenGetBooksByTitle_thenOK() {
    Book book = createRandomBook();
    createBookAsUri(book);
    Response response = RestAssured.get(
      API_ROOT + "/title/" + book.getTitle());
    
    assertEquals(HttpStatus.OK.value(), response.getStatusCode());
    assertTrue(response.as(List.class)
      .size() > 0);
}
@Test
public void whenGetCreatedBookById_thenOK() {
    Book book = createRandomBook();
    String location = createBookAsUri(book);
    Response response = RestAssured.get(location);
    
    assertEquals(HttpStatus.OK.value(), response.getStatusCode());
    assertEquals(book.getTitle(), response.jsonPath()
      .get("title"));
}

@Test
public void whenGetNotExistBookById_thenNotFound() {
    Response response = RestAssured.get(API_ROOT + "/" + randomNumeric(4));
    
    assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode());
}

Next, we’ll test creating a new book:

@Test
public void whenCreateNewBook_thenCreated() {
    Book book = createRandomBook();
    Response response = RestAssured.given()
      .contentType(MediaType.APPLICATION_JSON_VALUE)
      .body(book)
      .post(API_ROOT);
    
    assertEquals(HttpStatus.CREATED.value(), response.getStatusCode());
}

@Test
public void whenInvalidBook_thenError() {
    Book book = createRandomBook();
    book.setAuthor(null);
    Response response = RestAssured.given()
      .contentType(MediaType.APPLICATION_JSON_VALUE)
      .body(book)
      .post(API_ROOT);
    
    assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatusCode());
}

Update an existing book:

@Test
public void whenUpdateCreatedBook_thenUpdated() {
    Book book = createRandomBook();
    String location = createBookAsUri(book);
    book.setId(Long.parseLong(location.split("api/books/")[1]));
    book.setAuthor("newAuthor");
    Response response = RestAssured.given()
      .contentType(MediaType.APPLICATION_JSON_VALUE)
      .body(book)
      .put(location);
    
    assertEquals(HttpStatus.OK.value(), response.getStatusCode());

    response = RestAssured.get(location);
    
    assertEquals(HttpStatus.OK.value(), response.getStatusCode());
    assertEquals("newAuthor", response.jsonPath()
      .get("author"));
}

And delete a book:

@Test
public void whenDeleteCreatedBook_thenOk() {
    Book book = createRandomBook();
    String location = createBookAsUri(book);
    Response response = RestAssured.delete(location);
    
    assertEquals(HttpStatus.OK.value(), response.getStatusCode());

    response = RestAssured.get(location);
    assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode());
}

10. Conclusion

This was a quick but comprehensive intro to Spring Boot.

We of course barely scratched the surface here – there’s a lot more to this framework that we can cover in a single intro article.

What are Spring Boot common annotations
30
Mar
2021

What are Spring Boot Common Annotations?

The most commonly used and important spring boot annotations are as below:

  • @EnableAutoConfiguration – It enable auto-configuration mechanism.
  • @ComponentScan – enable component scanning in application classpath.
  • @SpringBootApplication – enable all 3 above three things in one step i.e. enable auto-configuration mechanism, enable component scanning and register extra beans in the context.
  • @ImportAutoConfiguration
  • @AutoConfigureBefore, @AutoConfigureAfter, @AutoConfigureOrder – shall be used if the configuration needs to be applied in a specific order (before of after).
  • @Conditional – annotations such as @ConditionalOnBean@ConditionalOnWebApplication or 
    @ConditionalOnClass allow to register a bean only when the condition meets.
What is auto-configuration? How to enable or disable certain configuration?
30
Mar
2021

What is Auto-Configuration? How to Enable or Disable Certain Configuration?

Spring boot auto configuration scans the classpath, finds the libraries in the classpath and then attempt to guess the best configuration for them, and finally configure all such beans.

Auto-configuration tries to be as intelligent as possible and will back-away as we define more of our own custom configuration. It is always applied after user-defined beans have been registered.

Auto-configuration works with help of @Conditional annotations such as @ConditionalOnBean and @ConditionalOnClass.

For example, look at AopAutoConfiguration class.

If class path scanning finds EnableAspectJAutoProxy, Aspect, Advice and AnnotatedElement classes and spring.aop.auto=false is not present in properties file then Spring boot will configure the Spring AOP module for us.

@Configuration @ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class }) @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true) public class AopAutoConfiguration {//code}
Convert a String to Date (LocalDate, LocalDateTime, ZonedDateTime, LocalTime) in Java
03
Mar
2021

Convert a String to Date (LocalDate, LocalDateTime, ZonedDateTime, LocalTime) in Java

In this article, you’ll find several examples demonstrating how to convert a date represented as a String to a Date, LocalDate, LocalDateTime, ZonedDateTime, or LocalTime instance in Java.

How to convert a String to Date in Java using SimpleDateFormat

Dates are represented as Strings by using some patterns and symbols. Java’s SimpleDateFormat class uses the following patterns and symbols for parsing a String to Date.

SimpleDateFormat Date and Time Patterns

Let’s see an example:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class SimpleDateFormatExample {
    public static void main(String[] args) {
        SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
        String dateStr = "2020-01-31";

        try {
            // Parsing a String to Date
            Date date = dateFormatter.parse(dateStr);
            System.out.println(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

Here is another example in which we parse a more complex date time representation:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class SimpleDateFormatExample {
    public static void main(String[] args) {
        SimpleDateFormat dateTimeFormatter = new SimpleDateFormat("E, MMM dd yyyy, hh:mm:ss a");
        String dateTimeStr = "Fri, Jan 31 2020, 10:30:45 PM";

        try {
            // Parse the String representation of date and time to Date
            Date date = dateTimeFormatter.parse(dateTimeStr);
            System.out.println(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

Convert/Parse a String to LocalDate

We can also use the DateTime API introduced in Java 8 to convert a String to an instance of various DateTime classes like LocalDate, LocalTime, LocalDateTime, ZonedDateTime etc.

The DateTime API has a DateTimeFormatter class that can be used to define the date time format and parse the String according to the specified date time format. The DateTimeFormatter class uses the following patterns and symbols:

DateTimeFormatter Patterns and Symbols

Let’s see an example of parsing a String to LocalDate:

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class LocalDateParseExample {
    public static void main(String[] args) {
        // Parse a String in ISO Date format (yyyy-MM-dd) to LocalDate
        LocalDate date1 = LocalDate.parse("2020-02-28");
        System.out.println(date1);

        // Parse a String in a custom date format to LocalDate using DateTimeFormatter
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
        LocalDate date2 = LocalDate.parse("28/02/2020", dateFormatter);
        System.out.println(date2);

        // Parse a String in a custom date-time format to LocalDate using DateTimeFormatter
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("E, MMM dd yyyy, hh:mm:ss a");
        LocalDate date3 = LocalDate.parse("Fri, Feb 14 2020, 10:20:50 PM", dateTimeFormatter);
        System.out.println(date3);

    }
}

Convert/Parse a String to LocalDateTime

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class LocalDateTimeParseExample {
    public static void main(String[] args) {
        // Parse a String in ISO DateTime format (yyyy-MM-ddTHH:mm:ss) to LocalDateTime
        LocalDateTime dateTime1 = LocalDateTime.parse("2020-01-31T10:15:30");
        System.out.println(dateTime1);

        // Parse a String in a custom date format to LocalDate using DateTimeFormatter
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("MMM dd yyyy, HH:mm");
        LocalDateTime dateTime2 = LocalDateTime.parse("Jan 11 2020, 10:30", dateTimeFormatter);
        System.out.println(dateTime2);

        // Parse a String in a custom DateTime format to LocalDateTime using DateTimeFormatter
        DateTimeFormatter descriptiveDateTimeFormatter = DateTimeFormatter.ofPattern("E, MMM dd yyyy, hh:mm:ss a");
        LocalDateTime dateTime3 = LocalDateTime.parse("Fri, Feb 14 2020, 10:20:50 PM", descriptiveDateTimeFormatter);
        System.out.println(dateTime3);

    }
}

Convert/Parse a String to LocalTime

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

public class LocalTimeParseExample {
    public static void main(String[] args) {
        // Parse a String in ISO Time format (HH:mm:ss) to LocalDate
        LocalTime time1 = LocalTime.parse("12:30:50");
        System.out.println(time1);

        // Parse a String in a custom date format to LocalTime using DateTimeFormatter
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
        LocalTime time2 = LocalTime.parse("28/02/2020 16:45:30", dateTimeFormatter);
        System.out.println(time2);

    }
}

Convert/Parse a String to ZonedDateTime

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class ZonedDateTimeParseExample {
    public static void main(String[] args) {
        // Parse a String in ISO DateTime format to ZonedDateTime
        ZonedDateTime dateTime1 = ZonedDateTime.parse("2020-01-31T10:15:30+01:00[Europe/Paris]");
        System.out.println(dateTime1);

        // Parse a String in a custom date time format to ZonedDateTime using DateTimeFormatter
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("MMM dd yyyy, HH:mm (VV)");
        ZonedDateTime dateTime2 = ZonedDateTime.parse("Jan 11 2020, 10:30 (America/Los_Angeles)", dateTimeFormatter);
        System.out.println(dateTime2);
    }
}
How to copy a File or Directory in Java
03
Mar
2021

How to copy a File or Directory in Java

In this article, you’ll learn how to copy a file or directory in Java using various methods like Files.copy() or using BufferedInputStream and BufferedOutputStream.

Java Copy File using Files.copy()

Java NIO’s Files.copy() method is the simplest way of copying a file in Java.

import java.io.IOException;
import java.nio.file.*;

public class CopyFileExample {
    public static void main(String[] args) {

        Path sourceFilePath = Paths.get("./bar.txt");
        Path targetFilePath = Paths.get(System.getProperty("user.home") + "/Desktop/bar-copy.txt");

        try {
            Files.copy(sourceFilePath, targetFilePath);
        } catch (FileAlreadyExistsException ex) {
            System.out.println("File already exists");
        } catch (IOException ex) {
            System.out.format("I/O error: %s%n", ex);
        }
    }
}

The Files.copy() method will throw FileAlreadyExistsException if the target file already exists. If you want to replace the target file then you can use the REPLACE_EXISTING option like this –

Files.copy(sourceFilePath, targetFilePath, StandardCopyOption.REPLACE_EXISTING);

Note that, Directories can be copied using the same method. However, files inside the directory are not copied, so the new directory will be empty even when the original directory contains files.

Read: How to copy Directories recursively in Java

Java Copy File using BufferedInputStream and BufferedOutputStream

You can also copy a file byte-by-byte using a byte-stream I/O. The following example uses BufferedInputStream to read a file into a byte array and then write the byte array using BufferedOutputStream.

You can also use a FileInputStream and a FileOutputStream directly for performing the reading and writing. But a Buffered I/O is more performant because it buffers data and reads/writes it in chunks.

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class CopyFileExample1 {
    public static void main(String[] args) {
        Path sourceFilePath = Paths.get("./bar.txt");
        Path targetFilePath = Paths.get(System.getProperty("user.home") + "/Desktop/bar-copy.txt");

        try(InputStream inputStream = Files.newInputStream(sourceFilePath);
            BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);

            OutputStream outputStream = Files.newOutputStream(targetFilePath);
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream)) {

            byte[] buffer = new byte[4096];
            int numBytes;
            while ((numBytes = bufferedInputStream.read(buffer)) != -1) {
                bufferedOutputStream.write(buffer, 0, numBytes);
            }
        } catch (IOException ex) {
            System.out.format("I/O error: %s%n", ex);
        }
    }
}
Create maven web project in eclipse step by step
30
Mar
2021

Create Maven Web Project In Eclipse

Learn to create maven web project in eclipse which we should be able to import on eclipse IDE for further development.

To create eclipse supported web project, we will need to create first a normal maven we application and then we will make it compatible to eclipse IDE.

1. Create maven web project in eclipse

Run this maven command to create a maven web project named ‘demoWebApplication‘. Maven archetype used is ‘maven-archetype-webapp‘.

$ mvn archetype:generate -DgroupId=com.howtodoinjava -DartifactId=demoWebApplication-DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

This will create maven web project structure and web application specific files like web.xml.

create-web-project-using-maven-1844893

2. Convert to eclipse dynamic web project

To Convert created maven web project to eclipse dynamic web project, following maven command needs to be run.

$ mvn eclipse:eclipse -Dwtpversion=2.0
convert-to-eclipse-webproject-9798545

Please remember that adding “-Dwtpversion=2.0” is necessary, otherwise using only “mvn eclipse:eclipse” will convert it to only normal Java project (without web support), and you will not be able to run it as web application.

3. Import web project in Eclipse

  1. Click on File menu and click on Import option.
    import-project-menu-9106164
  2. Now, click on “Existing project..” in general section.
    existing-project-menu-9473492
  3. Now, browse the project root folder and click OK. Finish.
    browse-project-menu-2890673
  4. Above steps will import the project into eclipse work space. You can verify the project structure like this.
    project-created-success-4773138

In this maven tutorial, we learned how to create maven dynamic web project in eclipse. In this example, I used eclipse oxygen. You may have different eclipse version but the steps to follow will be same.

Happy Learning !!

Deploying AND Hosting Spring Boot applications on Heroku
06
Feb
2021

Deploying / Hosting Spring Boot applications on Heroku

Heroku is a cloud platform as a service (PaaS) that you can use to deploy, manage, and scale your applications. It supports several programming languages and has a very simple and convenient deployment model.

In this article, you’ll learn how to deploy Spring Boot applications on Heroku with step by step instructions.

There are several ways of deploying spring boot apps to Heroku. I’ll take you through all of them one by one. I’ll also show you the pros and cons of using one method over the other.

Creating a Simple Spring Boot app for deployment

Before learning about deployment, Let’s first create a simple Spring Boot app that we’ll deploy on Heroku.

  1. Bootstrapping the appThe easiest way to bootstrap a new spring boot app is using Spring Boot CLI. Just enter the following command in your terminal to generate the project using Spring Boot CLI –$ spring init -n=heroku-demo -d=web heroku-demo Using service at https://start.spring.io Project extracted to '/Users/rajeevkumarsingh/heroku-demo' The command will generate the application with all the necessary configurations in a directory named heroku-demo.You can also use the Spring Initializr web app to generate the application if you don’t want to install Spring Boot CLI:
    • Open http://start.spring.io.
    • Enter heroku-demo in the artifact field.
    • Add Web in the dependencies section.
    • Click Generate Project to generate and download the project.
    This will downloaded a zip archive of the project with all the directories and configurations.
  2. Building a Demo endpointLet’s add a controller endpoint to the application so that it responds with a message whenever we access the root (/) endpoint.Create a file named IndexController.java inside src/main/java/com/example/herokudemo/, and add the following code to it –package com.example.herokudemo; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class IndexController { @GetMapping("/") public String index() { return "Hello there! I'm running."; } }
  3. Running the appYou can run the app using the following command –$ mvn spring-boot:run The app will start on port 8080 by default. You can access it by going to http://localhost:8080. You should see the message Hello there! I'm running..

Deploying a Spring Boot app on Heroku using Git and Heroku CLI

All right! Now that we have a sample app, let’s learn how to deploy it to Heroku. In this section we’ll deploy the app using Git and Heroku CLI.

You need to have a Heroku account. If you don’t have one, you can create it from Heroku’s Signup Page. Once you’re registered with Heroku, follow the steps below to deploy the Spring Boot app.

  1. Download and install Heroku CLIHeroku CLI is a command line application that lets you create, deploy and manage Heroku apps from the command line. You can download Heroku CLI from Heroku Dev Center. You’ll also find the instructions for installing Heroku CLI on your platform on that page.
  2. Login using your email and passwordEnter the following command to login to Heroku using Heroku CLIheroku login You will be prompted to enter your Heroku account’s email and password. Enter all the details –Enter your Heroku credentials: Email: youremail@example.com Password: ************** Logged in as youremail@example.com That’s it! You’re logged in to Heroku. We can now proceed with the deployment.
  3. Set up Git and create a Heroku appExecute the following commands from the root directory of the project to create a local Git repository for the project –git init git add . git commit -m "initial commit" Now, create a new heroku app using heroku create command like so –$ heroku create Creating app... done, ⬢ apple-custard-73702 https://apple-custard-73702.herokuapp.com/ | https://git.heroku.com/apple-custard-73702.git The above command creates a new heroku app and also makes a remote repository for the app at Heroku.Heroku chooses a random unique name for the app by default. If you want to specify a custom name then you can pass the app name in the heroku create command like this –heroku create <YOUR_UNIQUE_APP_NAME> You can also rename an existing app using heroku apps:rename command –heroku apps:rename --app <OLD_NAME> <NEW_NAME> Let’s use the above command to rename our app –$ heroku apps:rename --app apple-custard-73702 projectname-heroku-demo Renaming apple-custard-73702 to projectname-heroku-demo... done
    Git remote heroku updated ▸ Don't forget to update git remotes for all other local checkouts of the app.
  4. Deploy the app to HerokuFinally, you can deploy the app by simply pushing the code to the remote repository named heroku which was created by the heroku create command.git push heroku master Heroku automatically detects that the project is a Maven/Java app and triggers the build accordingly.The deployment will take some time. You should see the build logs on console. Once the application is deployed, you can open it using the following command –heroku open The app should open in the system’s default browser and you should see the message Hello there! I'm running..You can see the logs of the application anytime by running the following command –heroku logs --tail

This is the easiest method of deploying spring boot apps to Heroku. But its not the ideal option if –

  • Your project doesn’t use Git.
  • Your project is closed source and you don’t want to push the source code to Heroku.
  • Your project uses some dependencies that are internal to your company. In this case the source code won’t compile at Heroku because it won’t be able to download the internal dependencies.

Deploying a Spring Boot app on Heroku using Heroku CLI Deploy Plugin

Unlike the previous method, this heroku deployment method doesn’t require you to setup a Git repository for your project and push the source code to Heroku.

You can build and package the app locally and deploy the packaged jar file to Heroku. This is done using Heorku CLI Deploy Plugin. It allows you to deploy a pre-built jar or war file to Heroku.

Follow the steps below to deploy a Spring Boot app on Heroku using Heroku CLI Deploy plugin:

  1. Install Heroku Deploy CLI PluginEnter the following command in your terminal to install the heroku-cli-deploy plugin:heroku plugins:install heroku-cli-deploy
  2. Package your Spring Boot applicationNext, you’ll need to package the application in the form of a jar file. You can do that using mvn package command –mvn clean package This command will create a packaged jar file of the application in the target folder of the project.
  3. Create a Heroku appNow let’s create a Heroku app using heroku create command –heroku create <APP-NAME> --no-remote Notice the use of --no-remote option. This tells heroku to not setup any git remote repository.
  4. Deploy the jar file on HerokuFinally, you can deploy the jar file using the following command –heroku deploy:jar target/heroku-demo-0.0.1-SNAPSHOT.jar --app <APP-NAME> The above command should have deployed the app successfully. But there is a caveat here. Let’s check the logs of the application to understand that-heroku logs --tail --app <APP-NAME> If you check the logs using the above command, you’ll notice an error like this –Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 90 seconds of launch Heroku requires our application to listen on the port passed through $PORT environment variable. But the spring boot application listens on port 8080 by default.Fixing the Port binding errorThere are two ways of fixing the port binding error:
    • You can specify the port that the application should listen to in a property named server.port in the src/main/java/resources/application.properties file –server.port=${PORT:8080} The above property will evaluate to the PORT environment variable if it is available, otherwise it will fall back to the default port 8080.
    • Alternatively, you can use a Procfile to customize the command that is used to run the application. The customized command would include the server.port configuration that will be used by Spring Boot to bind the app.Create the Procfile in the root directory of the project with the following configuration –# Procfile web: java $JAVA_OPTS -jar target/heroku-demo-0.0.1-SNAPSHOT.jar -Dserver.port=$PORT $JAR_OPTS
    After doing one of the above two, You can deploy the app using the same heroku deploy:jar command –heroku deploy:jar target/heroku-demo-0.0.1-SNAPSHOT.jar --app <APP-NAME> This time the application should start successfully. You can verify that by opening the app in the browser using heroku open command.

Deploying a Spring Boot app on Heroku using Heroku Maven Plugin

The Heroku Maven plugin allows you to deploy any maven application without having to install Heroku CLI. It integrates the deployment into your build system, and works out of the box with any CI / CD server like Jenkins or Travis CI.

Follow the steps below to deploy your spring boot application using Heroku Maven Plugin.

  1. Adding and Configuring Heroku Maven Plugin
    Add the heroku-maven-plugin to the plugins section of your pom.xml file –
    Change the <appName>
  2.  configuration to an app name of your choice.
 -<build> <plugins> <!-- Heroku Maven Plugin Configuration --> <plugin> <groupId>com.heroku.sdk</groupId> <artifactId>heroku-maven-plugin</artifactId> <version>2.0.3</version> <configuration> <appName>project-heroku-maven-demo</appName> <includeTarget>false</includeTarget> <includes> <include>${project.build.directory}/${project.build.finalName}.jar</include> </includes> <jdkVersion>${java.version}</jdkVersion> <processTypes> <web>java $JAVA_OPTS -jar ${project.build.directory}/${project.build.finalName}.jar</web> </processTypes> </configuration> </plugin> </plugins> </build> Change the <appName>
  1. Creating the app
  2. You can create an app either using Heroku CLI or from the Heroku dashboard. To create the app using Heroku CLI, type the following command in your terminal –heroku create project-heroku-maven-demo Don’t forget to change the name project-heroku-maven-demo to the app name that you’ve set in the pom.xml file.If you don’t want to install Heroku CLI, then create the app from the dashboard.
  3. Deploying the app using Heroku maven plugin
  4. If you have Heroku CLI installed, then use the following maven command to deploy the application –mvn clean heroku:deploy If you don’t have Heroku CLI installed, then you need to set the HEROKU_API_KEY environment variable before running heroku:deploy –HEROKU_API_KEY="YOUR HEROKU API KEY" mvn clean heroku:deploy You can find the API key from Heroku dashboard.

Thanks for reading. In the next article, you’ll learn how to deploy spring boot applications on AWS using Elastic beanstalk. Also, Don’t forget to subscribe to my newsletter to get notified whenever I publish new articles on the blog.

Simple Way to Monitor the Spring Boot Apps
18
Mar
2021

Simple Way to Monitor the Spring Boot Apps

Administration of spring boot applications using spring boot admin.

This includes health status, various metrics, log level management, JMX-Beans interaction, thread dumps and traces, and much more. Spring Boot Admin is a community project initiated and maintained by code-centric.

Spring boot admin will provide UI to monitor and do some administrative work for your spring boot applications.

This project has been started by codecentric and its open source. You can do your own customization if you want to.

Git Repo: Link

The above video will give you a better idea of what is this project, so we will directly start with an example.

Spring Boot provides actuator endpoints to monitor metrics of individual microservices. These endpoints are very helpful for getting information about applications like if they are up if their components like database etc are working well. But a major drawback or difficulty about using actuator endpoints is that we have to individually hit the endpoints for applications to know their status or health. Imagine microservices involving 150 applications, the admin will have to hit the actuator endpoints of all 150 applications. To help us to deal with this situation we are using Spring Boot Admin app.

Sample Code:

To implement this we will create two projects one is server and another is the client.

  1. Spring Boot Admin server.
  2. Spring Boot Admin client.

Spring Boot Admin Server:

The project structure should look like any spring boot application:

POM.xml

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

<groupId>com.techwasti</groupId>
<artifactId>spring-boot-admin</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spring-boot-admin</name>
<description>Demo project for Spring Boot</description>

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

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

<dependencies>
<!-- admin dependency-->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui-login</artifactId>
<version>1.5.1</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server</artifactId>
<version>1.5.1</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui</artifactId>
<version>1.5.1</version>
</dependency>
<!-- end admin dependency-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

</dependencies>

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


</project>

We need to configure security as well since we are accessing sensitive information:

package com.techwasti;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import de.codecentric.boot.admin.config.EnableAdminServer;

@EnableAdminServer
@Configuration
@SpringBootApplication
public class SpringBootAdminApplication {

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

@Configuration
public static class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().loginPage("/login.html").loginProcessingUrl("/login").permitAll();
http.logout().logoutUrl("/logout");
http.csrf().disable();

http.authorizeRequests().antMatchers("/login.html", "/**/*.css", "/img/**", "/third-party/**").permitAll();
http.authorizeRequests().antMatchers("/**").authenticated();

http.httpBasic();
}
}

}

application.propertie file content

spring.application.name=SpringBootAdminEx
server.port=8081
security.user.name=admin
security.user.password=admin

Run the app and localhost:8081

Enter username and password and click on login button

As this is a sample example so we hardcoded username and password but you can use spring security to integrate LDAP or any other security.

Spring Boot Admin can be configured to display only the information that we consider useful.

spring.boot.admin.routes.endpoints=env, metrics, trace, info, configprops

Notifications and Alerts:

We can notify and send alerts using any below channels.

  • Email
  • PagerDuty
  • OpsGenie
  • Hipchat
  • Slack
  • Let’s Chat

Spring Boot Admin Client:

Now we are ready with the admin server application let us create the client application. Create any HelloWorld spring boot application or if you have any existing spring boot app you can use the same as a client application.

Add below Maven dependency

<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>1.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Next, update application.properties and add the following properties

spring.boot.admin.url=http://localhost:8081
spring.boot.admin.username=admin
spring.boot.admin.password=admin

These changes are fine in your client application now run the client application. Once the client application is up and running go and check your admin server application. It will show all your applications.

Beautiful Dashboards:

Spring boot admin is providing a wide range of features. As part of this article, we have just listed very few dig deeper and explore more.