[Code Pipeline x Elastic Beanstalk] CI / CD Java application to Elastic Beanstalk with Code Pipeline Part 1

Hands-on to CI / CD Java application (gets value from database and returns result in JSON format) to Elastic Beanstalk with CodePipeline.

Because it's long

-[Code Pipeline x Elastic Beanstalk] CI / CD Java application to Elastic Beanstalk with Code Pipeline Part 1 (this article)
→ 1. Create Java (Spring Boot) application, 2. Create Git repository -[Code Pipeline x Elastic Beanstalk] CI / CD Java application to Elastic Beanstalk with Code Pipeline Part 2
→ 3. Create Elastic Beanstalk environment, 4. Database settings -[Code Pipeline x Elastic Beanstalk] CI / CD Java application to Elastic Beanstalk with Code Pipeline Part 3
→ 5. Creating a pipeline, 6. Cleaning up

The content is divided into 3 articles. The total working time is assumed to be around 3 hours, excluding advance preparation. About 1 hour for each article is a guide.

environment

Image diagram

This is a rough image of what you can do with this procedure. When you push the locally edited code to Commit → CodeCommit, CodePipeline builds a CI / CD mechanism that automatically builds and deploys to Elastic Beanstalk, which is the application execution environment. image.png

Advance preparation

① If Eclipse is not installed, Pleiades All in One Eclipse Download (Release 2020-06) | MergeDoc Project Please download the latest version (as of August 3, 2020). With STS and Lombok already configured, you can start developing with Spring Boot right away.

(2) JDK assumes Amazon Correto 8. Download Amazon Corretto 8 | aws Please download → install and complete the settings for Eclipse.

③ So that CodeCommit can be used How to use AWS CodeCommit. From where you make an access key. | Qiita Please make preparations in advance by referring to.

procedure

1. Creating a Java (Spring Boot) application

First, create a Java (Spring Boot) application in Eclipse. It is very simple to get detailed store information based on the store ID. The structure of the package is as follows.

sample-eb-java
     |
    src/main/java
     |----jp.co.sample_eb_java
     |               |---- app
     |               |     |---- controller
     |               |     |---- response
     |               |
     |               |---- domain
     |               |      |---- service
     |               |      |---- repository
     |               |      |---- model
     |               |  
     |               |---- exception
     |               
     |
    src/main/resources
     |----application.yml
     |
    Buildfile
    Procfile
    build.gradle
    .ebextensions

・ ・ ・ Omitted below

[1] Creating a project

(1) After opening Eclipse, click "File" (①)> "New" (②)> "Project" (③). image.png

(2) When the wizard starts up, select "Spring Starter Project" (①) and click "Next" (②). image.png

(3) Set as shown in the capture below (①) and click "Next" (②). image.png

(4) Check Lombok, MySQL Driver, Spring Data JPA, Spring Web (search from the search box if it is not a candidate) and click "Next" (②). image.png

[2] Change / create configuration file

Next, change and create the configuration file.

(1) Rename application.properties to application.yml and write as follows.

application.yml


spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://${DB_HOST:hostname}:${DB_PORT:port number}/${DB_NAME:Database name}?serverTimezone=JST
    username: ${DB_USERNAME:username}
    password: ${DB_PASSWORD:password}
  jpa:
    database: MYSQL
    hibernate.ddl-auto: none
server:
  port: ${SERVER_PORT:Server port number}

I will change the Japanese part later, so leave it as it is.

(2) Create a Buildfile in the root directory and describe as follows.

Buildfile


build: ./gradlew assemble

(3) Create a Procfile in the root directory and describe as follows.

Procfile


web: java -jar build/libs/sample-eb-java.jar

(4) Add to build.gradle. Find the part before the addition and add "bootJar.archiveName =" sample-eb-java.jar "".

build.gradle (before addition)


configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

build.gradle (after addition)


configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
	bootJar.archiveName = "sample-eb-java.jar"
}

[3] Creating source code

Next is the creation of the source code. The package structure is as shown at the beginning of this chapter (1. Creating a Java (Spring Boot) application), but I think it would be troublesome to go back to the top, so I will post a capture. image.png

(1) First, the Entity class. It will come out later, but it corresponds to the shop_informations table created in the database.

ShopInformation.java


package jp.co.sample_eb_java.domain.model;

import java.io.Serializable;
import javax.persistence.*;

import lombok.Getter;
import lombok.Setter;


/**
 *Entity class linked to the store information table
 * 
 * @author CHI-3
 *
 */
@Entity
@Getter
@Setter
@Table(name="shop_informations")
@NamedQuery(name="ShopInformation.findAll", query="SELECT s FROM ShopInformation s")
public class ShopInformation implements Serializable {
	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="shop_id")
	private Integer shopId;

	private String access;

	private String address;

	@Column(name="business_hour")
	private String businessHour;

	@Column(name="regular_holiday")
	private String regularHoliday;

	@Column(name="shop_name")
	private String shopName;

	private String tel;

	@Column(name="zip_code")
	private String zipCode;

	public ShopInformation() {
	}

}

(2) Next, the Repository interface. The interface used for table operations.

ShopInformationRepository.java


package jp.co.sample_eb_java.domain.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import jp.co.sample_eb_java.domain.model.ShopInformation;

/**
 *Repository interface for handling store information
 *
 * @author CHI-3
 *
 */
@Repository
public interface ShopInformationRepository extends JpaRepository<ShopInformation, Integer>{

	/**
	 *Acquire store information linked to store ID
	 *
	 * @param shopId Store ID
	 * @return store information
	 */
	public ShopInformation findByShopId(Integer shopId);

}

(3) Next, the Service class. Provides logic.

ShopInformationService.java


package jp.co.sample_eb_java.domain.service;

import java.util.Optional;

import org.springframework.stereotype.Service;

import jp.co.sample_eb_java.domain.model.ShopInformation;
import jp.co.sample_eb_java.domain.repository.ShopInformationRepository;
import lombok.RequiredArgsConstructor;

/**
 *Service class to acquire store information
 *
 * @author CHI-3
 *
 */
@Service
@RequiredArgsConstructor
public class ShopInformationService {

	private final ShopInformationRepository shopInformationRepository;

	/**
	 *Get store information
	 *
	 * @param shopId Store ID
	 * @return store information
	 * @throws Exception
	 */
	public ShopInformation getShopInformation(Integer shopId) throws Exception{
		//Get store information: Throw exception if target store does not exist
		ShopInformation shopInformation = Optional.ofNullable(shopInformationRepository.findByShopId(shopId)).orElseThrow(Exception::new);
		return shopInformation;
	}

}

(4) Next, the Response class. We will mold the value to be returned.

ShopInformationResponse.java


package jp.co.sample_eb_java.app.response;

import jp.co.sample_eb_java.domain.model.ShopInformation;
import lombok.Builder;
import lombok.Getter;

/**
 *Response class for store information acquisition
 * @author CHI-3
 *
 */
@Getter
@Builder
public class ShopInformationResponse {
	/**store information*/
	private ShopInformation shopInformation;
}

(5) Next is the Controller class. I am using @RestController to return the value in JSON format.

ShopInformationController.java


package jp.co.sample_eb_java.app.controller;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import jp.co.sample_eb_java.app.response.ShopInformationResponse;
import jp.co.sample_eb_java.domain.model.ShopInformation;
import jp.co.sample_eb_java.domain.service.ShopInformationService;
import lombok.RequiredArgsConstructor;

/**
 *API to get store information
 * 
 * @author CHI-3
 *
 */
@RestController
@RequiredArgsConstructor
public class ShopInformationController {

	private final ShopInformationService shopInformationService;

	/**
	 *Get store information
	 *
	 * @param shopId Store ID
	 * @return Response entity (store information)
	 * @throws Exception
	 */
	@GetMapping("/shop-information/{shopId}")
	public ResponseEntity<ShopInformationResponse> getShopInformation(@PathVariable("shopId") Integer shopId) throws Exception{
		ShopInformation shopInformation = shopInformationService.getShopInformation(shopId);
		ShopInformationResponse shopInformationResponse = ShopInformationResponse.builder().shopInformation(shopInformation).build();
		return new ResponseEntity<>(shopInformationResponse, HttpStatus.OK);
	}

}

(6) Finally, ExceptionHandler. This class handles errors. This time, when requesting a store ID that does not exist, a 400 error (Bad Request) will be returned.

ExceptionHandler.java


package jp.co.sample_eb_java.exception;


import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import lombok.extern.slf4j.Slf4j;

/**
 *Exception handler
 *
 * @author CHI-3
 *
 */
@ControllerAdvice
@Slf4j
public class ExceptionHandler {

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @org.springframework.web.bind.annotation.ExceptionHandler({Exception.class})
    public @ResponseBody
    ResponseEntity<Object> handleError(final Exception e) {
        log.info("call ExceptionHandler", e);
        return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
    }
}

[4] Creating a jar file

(1) Create a jar file for creating the Elastic Beantalk environment. Go to the root directory of your project and run the build command. If the layout is as follows, it will be as follows. image.png

Execution command


> cd C:\pleiades-2020-03\workspace\sample-eb-java
> gradlew build

OK if the result is "BUILD SUCCESSFUL"! </ b>

(2) Make sure that the jar file is created under build / libs. (Use this when creating the environment.) image.png

2. Create a Git repository

Allow Git management for the project created in step 1. Create a local repository on the project created in step 1 and synchronize with the remote repository.

[1] Creating a local repository

(1) Open a command prompt or GitBash, move (cd) to the root directory of the project created in 1, and then initialize the repository (execute the following command).

> git init

It's OK if you have a .git folder in the root directory of your project. image.png

(2) Rewrite .gitignore before committing. With the default description, necessary functions such as STS will not be committed, which may cause problems such as not working later. Change the description as follows. Those that do not need to be committed, such as class files and lock files, are excluded.

.gitignore


bin/*
.lock

(3) Commit your edits. It is OK if you execute the following commands in order.

> git add .
> git commit -m "first commit"

The "first commit" part (commit message) can be any content, but let's make it a message that understands the edited content (same below)

(4) Edit .gitignore again. Describe what is necessary as a function but is not subject to commit from local.

.gitignore



# Created by https://www.toptal.com/developers/gitignore/api/java,gradle,eclipse
# Edit at https://www.toptal.com/developers/gitignore?templates=java,gradle,eclipse

### Eclipse ###
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders

# External tool builders
.externalToolBuilders/

# Locally stored "Eclipse launch configurations"
*.launch

# PyDev specific (Python IDE for Eclipse)
*.pydevproject

# CDT-specific (C/C++ Development Tooling)
.cproject

# CDT- autotools
.autotools

# Java annotation processor (APT)
.factorypath

# PDT-specific (PHP Development Tools)
.buildpath

# sbteclipse plugin
.target

# Tern plugin
.tern-project

# TeXlipse plugin
.texlipse

# STS (Spring Tool Suite)
.springBeans

# Code Recommenders
.recommenders/

# Annotation Processing
.apt_generated/
.apt_generated_test/

# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet

# Uncomment this line if you wish to ignore the project description file.
# Typically, this file would be tracked if it contains build/dependency configurations:
#.project

### Eclipse Patch ###
# Spring Boot Tooling
.sts4-cache/

### Java ###
# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

### Gradle ###
.gradle
build/

# Ignore Gradle GUI config
gradle-app.setting

# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar

# Cache of project
.gradletasknamecache

# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties

### Gradle Patch ###
**/build/

# End of https://www.toptal.com/developers/gitignore/api/java,gradle,eclipse
  • Java
  • Gradle
  • Eclipse

Was created as a condition.

When you're done making changes, commit them.

> git add .gitignore
> git commit -m "fix .gitignore"

(5) Change the authority of gradlew. [Important] If you skip this step, an error will occur during build and deployment, so be sure to perform it. </ b>

First, check the permissions of gradlew.

> git ls-files -s gradlew
100644 fbd7c515832dab7b01092e80db76e5e03fe32d29 0       gradlew

If you keep the above, you only have read permission, so execute the following command to give execute permission.

> git update-index --add --chmod=+x gradlew

After the execution is completed, let's check the authority again. As shown below, it is OK if the last 3 digits of the first 6 digits are 755.

> git ls-files -s gradlew
100755 fbd7c515832dab7b01092e80db76e5e03fe32d29 0       gradlew

Commit your changes.

> git add gradlew
> git commit -m "fix permission of gradlew"

[2] Creating a remote repository

(1) Log in to the AWS Management Console, search for CodeCommit from "Services" (①), and click it (②). image.png

(2) After moving to the CodeCommit page, click "Create Repository". image.png

(3) Give the repository name the same as the project created in step 1 (1) and click "Create". image.png

[3] Synchronize local and remote repositories

(1) When the remote repository creation is completed, the following page will be displayed. Click "URL Clone" (1)> "HTTPS Clone" (2). image.png

(2) Open a command prompt or GitBash and execute the following command in the root directory of the project.

> git remote add origin (1)URI copied in "HTTPS Clone"

(3) Push the contents to the master of the remote repository.

> git push -u origin master

(4) On the console, check that the contents of the local repository are reflected in the remote repository. image.png

About the sequel

The continuation

-[Code Pipeline x Elastic Beanstalk] CI / CD Java application to Elastic Beanstalk with Code Pipeline Part 2

At. 3. Create Elastic Beanstalk environment, 4. Set up database connection.

Change log

--2020/08/13: Added "[4] Create jar file" to the procedure "1. Create Java (Spring Boot) application"

reference

environment

-Java SE Platform History | aws

procedure

1. Creating a Java (Spring Boot) application

-Introduction to master management application with SpringBoot + Vue.js + ElementUI + Firebase | Qiita

2. Create a Git repository

-[GitHub] Until you "push" the locally created repository remotely! | Qiita -git: How to check the permissions of a file on git Note | Qiita -Change the execution permission (permission) of files managed by Git | Magmag Git Note

Recommended Posts