Eric Rybarczyk

Eric Rybarczyk

Enthusiastic and motivated software engineer with diverse experience

AWS & Java Certified Developer

Spring Bike Clinic Website

Spring MVC Website hosted on AWS

Eric Rybarczyk

Spring Bike Clinic Website Screenshot

This project involves a Spring MVC website hosted on Amazon Web Services, and was formerly online at SpringBikeClinic.com. The application is built with Spring Boot and Spring MVC, using Java 11. Data access is implemented with Spring Data JPA and Hibernate. Spring Security provides the authentication and authorization framework. The UI is implemented with Thymeleaf and Bootstrap. You can find the source code in this GitHub repository.

Update: My ‘free for a year’ AWS usage plan came to an end, leading me to discontinue the production deployment of this project. This project is a traditional website backed by a relational database. The AWS hosting costs, particularly for the RDS instance, is not worth it to me for the sake of a personal demo project.

I have plans to overhaul the project to make it more cost-effective on AWS, such as shifting to DynamoDB for the database, which should allow me to operate the site within the ‘always free’ AWS tier for DynamoDB.
I’ll update this article at that time.

After completing my Java 11 certification in June 2020, I started focusing my personal learning time on Spring Framework and it’s related projects. Although I have learned a lot from a variety of good sources, I wanted to do something that included multiple technical aspects like security, data access, and deployment. In other words, a real website.

While the Spring Pet Clinic reference application provided the initial inspiration for this project, the work here is my own. For example, the site navigation design is clearly borrowed, but it is also an independent implementation - not a copy & paste exercise.

User Account Functionality

I spent a good deal of the development time focusing on the user account creation and maintenance. This is built on the capabilities provided by the Spring Security project, and is expanded to include my specific requirements for a Customer user type on the site.

For example, I wanted to ensure that a person creating a login on the site uses a valid email address, as well as an email address that they have access to. To accomplish this, I implemented a registration flow that initially creates the UserDetails.isEnabled() set to false.

Next, an email is sent to the address provided, containing a link the user must click to verify their access to this email address. Rather than handle this directly in the controller, I chose to use the event publishing capabilities in Spring, as shown below.

eventPublisher.publishEvent(new OnRegistrationCompleteEvent(this, createdUser, VERIFICATION_PATH));

The OnRegistrationCompleteEvent is the handled by an ApplicationListener<E> implementation.

@Component
@RequiredArgsConstructor
public class RegistrationListener implements ApplicationListener<OnRegistrationCompleteEvent> {

  private final UserVerificationService userVerificationService;

  @Override
  public void onApplicationEvent(OnRegistrationCompleteEvent event) {
    userVerificationService.initiateUserVerification(event.getUser(), event.getVerificationPath());
  }
}

The UserVerificationService generates a unique token string as part of the URL emailed to the address provided. Assuming the user is able to access this email message and clicks the link, they return to the site and the token is handled by the same UserVerificationService. If the token is legitimate and non-expired, the user account is enabled.

Automated Testing

I also put a lot of effort into the automated tests aspect of the project. This included Spring testing constructs like @WebMvcTest and @SpringBootTest as well as frameworks like Mockito and AssertJ.

Since a significant portion of the site code involves the creation and management of user accounts, this is also an important area for testing. I made use of nested classes and JUnit’s @Nested feature to keep all these tests organized.

Combined with JUnit’s @DisplayName annotation, this results in a clear organization in the test results, as you can see below.

Test Results display in IntelliJ

Although simple, tests like the example below are valuable because they ensure we don’t inadvertently change the Spring Security configuration and allow unauthenticated access to what should be a protected resource. By simply not including anything like the @WithMockUser annotation on the test it runs without authentication which is exactly the intent here.

@Test
void getAccountBikes_asAnonymousUser_isForbidden() throws Exception {
  mockMvc.perform(get(GET_BIKES_BASE_PATH))
         .andExpect(status().isUnauthorized());
  verifyNoInteractions(bikeService);
}

Likewise, ensuring request paths that are meant to be public are also working for authenticated users seemed like low-hanging fruit on the automated testing tree.

@WithMockUser("authenticatedUser")
@Test
void getIndexAsAuthenticatedUser_IsOk() throws Exception {
  mockMvc.perform(get(GET_INDEX_PATH))
         .andExpect(status().isOk())
         .andExpect(view().name(EXPECTED_GET_INDEX_VIEW_NAME));
}

While @WithMockUser is sufficient for some tests, in most cases I needed a mock instance of the custom UserDetails implementation I created. Learning how to generate this mock and make it available with a custom annotation was quite interesting and is the subject of a separate blog post.

Amazon Web Services

I also took this opportunity to gain realistic experience using a variety of AWS services for hosting all aspects of the site. I had not previously worked with cloud technologies like this, and I definitely enjoyed learning more in this area.

Originally, SpringBikeClinic.com was hosted on Amazon Web Services (AWS) and used the Elastic Beanstalk service in particular. The Spring application JAR was running on Amazon’s Corretto 11, on 64bit Amazon Linux 2.

Additional AWS services utilized included:

  • IAM: Identity & Access Management
  • EC2: Elastic Compute Cloud
  • RDS: Relational Database Service
  • SES: Simple Email Service
  • SNS: Simple Notification Service
  • Amazon Route 53 for DNS

I spent a considerable amount of time on this project, and I learned a lot. Call me strange, but I enjoyed it too. I even have plans for future expansion, but that project is a story for another day.


Article Series

Software Projects

Recent Posts

Categories

About

Enthusiastic and motivated software engineer