Spring boot Jar act different when build on AWS codebuild with gradlew
토이프로젝트를 진행하다가 도저히 풀리지 않는 문제가 있어서 stackoverflow
에 질문을 올린 원문이다.
질문을 영어로 올려서인지 의도가 잘 전달이 안된 모양이다. 답변이 다 뻔한 소리만 하고있어서…
원인은 찾아냈지만, 근본적인 해답은 찾지 못했다.
원인은 aws codebuild 로 gradlew bootJar 로 jar 를 만들었을때, 내부적으로 묶인 순서가 달라서 이다.
아래에도 써놨는데 왜 달라졌는지는 모르겠다.
해결은 spring @Configuration 에 의존하지말고, EnvironmentPostProcessor
을 사용하여, 모든 Bean 설정 보다 먼저 환경병수가 설정되도록 했다.
원문
I have weird situation now..
Spring boot, Graldew, Github, Aws Codebuild
and this is my humble git repository..
my project structure below.
.
├── build.gradle
├── settings.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat
└── src
│ └── main
│ ├── java
│ │ └── app
│ │ ├── config //config package
│ │ │ ├── VaultConfiguration //configuration for vault properties
│ │ │ └── TestConfA //configuration for test
│ │ ├── controller //controller package
│ │ ├── ...
│ │ └ MainApp.java //Spring Boot Main java
│ └── resources
│ ├── app
│ │ └── mapper
│ │ └── ... //mybatis xml path
│ ├── application.yaml
│ └── logback-spring.xml //logback config xml
└ .... project files ...
VaultConfiguration.java
package app.config;
import ...
@Configuration
@Order
public class VaultConfiguration {
private Logger logger = LoggerFactory.getLogger(VaultConfiguration.class);
public enum VAULT_ENVIRONMENT_KEY {
... enums
}
public enum VAULT_KEY {
... enums
}
public enum SPRING_DATABASE_PROPERTY_KEY {
... enums
}
public VaultConfiguration(Environment environment) throws URISyntaxException {
...
/*
* below code do
* System.setProperty("spring.datasource.url", url from vault)
* System.setProperty("spring.datasource.username", username from vault)
* System.setProperty("spring.datasource.password", password from vault)
* for spring datasource
*/
VaultTemplate vaultTemplate = new VaultTemplate(VaultEndpoint.from(new URI(vaultEndpoint.get())), new TokenAuthentication(vaultToken.get()));
Optional<VaultResponse> vaultResponse = Optional.of(vaultTemplate.read(vaultPathDatabaseInfo.get()));
logger.debug("vault read -> {}", vaultResponse.get().getData());
setSystemPropertyFromVaultDataByKey(SPRING_DATABASE_PROPERTY_KEY.URL, vaultResponse, VAULT_KEY.URL);
setSystemPropertyFromVaultDataByKey(SPRING_DATABASE_PROPERTY_KEY.USERNAME, vaultResponse, VAULT_KEY.USERNAME);
setSystemPropertyFromVaultDataByKey(SPRING_DATABASE_PROPERTY_KEY.PASSWORD, vaultResponse, VAULT_KEY.PASSWORD);
}
private void setSystemPropertyFromVaultDataByKey(SPRING_DATABASE_PROPERTY_KEY springDatabasePropertyKey, Optional<VaultResponse> vaultResponse, VAULT_KEY vaultKey) {
System.setProperty(springDatabasePropertyKey.value(), String.valueOf(vaultResponse.get().getData().get(vaultKey.value())));
}
}
TestConfA.java
just class for configuration load check
@Configuration
public class TestConfA {
public TestConfA() {
System.out.println(this.getClass().getSimpleName());
}
}
Simple example for me to study spring boot with gradle.
When build myself on local or ec2(amazon linux2) works find. just same sources.
build process like below..
$ git clone <my-git-hub-repository-url>
$ chmod +x ./gradlew
$ ./gradlew bootJar
$ java -jar ./build/libs/<my-boot-jar>.jar
$ java -version
openjdk version "13.0.2" 2020-01-14
OpenJDK Runtime Environment (build 13.0.2+8)
OpenJDK 64-Bit Server VM (build 13.0.2+8, mixed mode, sharing)
$ java -jar ./build/libs/GradleSpringBootMybatis-1.0-SNAPSHOT.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.6.RELEASE)
2020-04-28 02:56:41.451 INFO --- [ main ] app.MainApp : Starting MainApp on my-ip.ap-northeast-2.compute.internal with PID 1966 (/home/ec2-user/deleteme/GradleSpringBootMybatis/build/libs/GradleSpringBootMybatis-1.0-SNAPSHOT.jar started by ec2-user in /home/ec2-user/deleteme/GradleSpringBootMybatis)
...
2020-04-28 02:56:44.618 INFO --- [ main ] o.s.w.c.ContextLoader : Root WebApplicationContext: initialization completed in 3026 ms
TestConfA$$EnhancerBySpringCGLIB$$2c9c0e05
TestConfB$$EnhancerBySpringCGLIB$$64feb126
TestConfC$$EnhancerBySpringCGLIB$$9d615447
...
2020-04-28 02:56:45.157 ERROR --- [ main ] o.s.b.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'vaultConfiguration' defined in URL [jar:file:/home/ec2-user/deleteme/GradleSpringBootMybatis/build/libs/GradleSpringBootMybatis-1.0-SNAPSHOT.jar!/BOOT-INF/classes!/app/config/VaultConfiguration.class]: Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [app.config.VaultConfiguration$$EnhancerBySpringCGLIB$$4ca1e1fc]: Constructor threw exception; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:314)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:295)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1358)
Upper error is normal. because VaultConfiguration
needs OS environment. and java code like below.
Optional.of(System.getenv(VAULT_ENVIRONMENT_KEY.TOKEN.value()));
So error is just find.
And, also I can see TestConfA
, TestConfB
, TestConfC
crerator log.
But.. jar file build with aws codebuild/gradlew doesn’t works.
buildspec.yml
Codebuild below.
version: 0.2
phases:
install:
commands:
... print info for phases
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- unzip awscliv2.zip
- ./aws/install
build:
commands:
... print info for phases
- chmod +x ./gradlew
- ./gradlew bootJar
post_build:
commands:
- aws s3 sync ./build/libs/ s3://my-s3-bucket
cache:
paths:
- '/root/.gradle/caches/**/*'
excute jar log form s3 below.
$ java -jar GradleSpringBootMybatis-1.0-SNAPSHOT.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.6.RELEASE)
2020-04-28 03:03:03.754 INFO --- [ main ] app.MainApp : Starting MainApp on ip-my-ip.ap-northeast-2.compute.internal with PID 2090 (/home/ec2-user/deleteme/build/libs-20200427-185700/GradleSpringBootMybatis-1.0-SNAPSHOT.jar started by ec2-user in /home/ec2-user/deleteme/build/libs-20200427-185700)
2020-04-28 03:03:03.762 DEBUG --- [ main ] app.MainApp : Running with Spring Boot v2.2.6.RELEASE, Spring v5.2.5.RELEASE
2020-04-28 03:03:03.762 INFO --- [ main ] app.MainApp : No active profile set, falling back to default profiles: default
2020-04-28 03:03:06.860 INFO --- [ main ] o.s.b.w.e.t.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-04-28 03:03:06.884 INFO --- [ main ] o.a.c.h.Http11NioProtocol : Initializing ProtocolHandler ["http-nio-8080"]
2020-04-28 03:03:06.886 INFO --- [ main ] o.a.c.c.StandardService : Starting service [Tomcat]
2020-04-28 03:03:06.886 INFO --- [ main ] o.a.c.c.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.33]
2020-04-28 03:03:07.013 INFO --- [ main ] o.a.c.c.C.[.[.[/] : Initializing Spring embedded WebApplicationContext
2020-04-28 03:03:07.017 INFO --- [ main ] o.s.w.c.ContextLoader : Root WebApplicationContext: initialization completed in 3123 ms
2020-04-28 03:03:07.689 WARN --- [ main ] o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'postgreSQLRunner': Unsatisfied dependency expressed through field 'testMapper'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testMapper' defined in URL [jar:file:/home/ec2-user/deleteme/build/libs-20200427-185700/GradleSpringBootMybatis-1.0-SNAPSHOT.jar!/BOOT-INF/classes!/app/mapper/TestMapper.class]: Unsatisfied dependency expressed through bean property 'sqlSessionFactory'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sqlSessionFactory' defined in class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class]: Unsatisfied dependency expressed through method 'sqlSessionFactory' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Failed to determine a suitable driver class
2020-04-28 03:03:07.698 INFO --- [ main ] o.a.c.c.StandardService : Stopping service [Tomcat]
2020-04-28 03:03:07.742 INFO --- [ main ] o.s.b.a.l.ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-04-28 03:03:07.749 ERROR --- [ main ] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Spring act like there is no @Configuration
java config..
There is no log for VaultConfiguration
, TestConfA
..
why is this different??
Some people told me that that source not works. because of application.yaml doesn’t have database information(url/username/password).. and Can’t config like that..
So I’ll show log when It build myself, run with OS enviroment variable.
Not modified Source. just ./gradlew bootJar
, java -jar ./build/libs/~~~.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.6.RELEASE)
2020-04-28 08:12:19.052 INFO --- [ main ] app.MainApp : Starting MainApp on f89a6da9a330 with PID 1 (/msa/GradleSpringBootMybatis-1.0-SNAPSHOT.jar started by root in /msa/GradleSpringBootMybatis)
2020-04-28 08:12:19.060 DEBUG --- [ main ] app.MainApp : Running with Spring Boot v2.2.6.RELEASE, Spring v5.2.5.RELEASE
2020-04-28 08:12:19.062 INFO --- [ main ] app.MainApp : No active profile set, falling back to default profiles: default
2020-04-28 08:12:22.421 INFO --- [ main ] o.s.b.w.e.t.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-04-28 08:12:22.446 INFO --- [ main ] o.a.c.h.Http11NioProtocol : Initializing ProtocolHandler ["http-nio-8080"]
2020-04-28 08:12:22.450 INFO --- [ main ] o.a.c.c.StandardService : Starting service [Tomcat]
2020-04-28 08:12:22.450 INFO --- [ main ] o.a.c.c.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.33]
2020-04-28 08:12:22.644 INFO --- [ main ] o.a.c.c.C.[.[.[/] : Initializing Spring embedded WebApplicationContext
2020-04-28 08:12:22.644 INFO --- [ main ] o.s.w.c.ContextLoader : Root WebApplicationContext: initialization completed in 3453 ms
TestConfA$$EnhancerBySpringCGLIB$$9c73851a
TestConfB$$EnhancerBySpringCGLIB$$d4d6283b
TestConfC$$EnhancerBySpringCGLIB$$d38cb5c
2020-04-28 08:12:23.201 DEBUG --- [ main ] a.c.VaultConfiguration : creator
2020-04-28 08:12:23.203 INFO --- [ main ] a.c.VaultConfiguration : system enviroment -> VAULT_ENDPOINT, ***my-vault-endpoint***
2020-04-28 08:12:23.207 INFO --- [ main ] a.c.VaultConfiguration : system enviroment -> VAULT_PATH_DATABASE_INFO, ***my-vault-database-path***
2020-04-28 08:12:23.207 INFO --- [ main ] a.c.VaultConfiguration : system enviroment -> PATH, /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
2020-04-28 08:12:23.207 INFO --- [ main ] a.c.VaultConfiguration : system enviroment -> VAULT_TOKEN, ***my-vault-token***
2020-04-28 08:12:23.208 INFO --- [ main ] a.c.VaultConfiguration : system enviroment -> HOSTNAME, f89a6da9a330
2020-04-28 08:12:23.208 INFO --- [ main ] a.c.VaultConfiguration : system enviroment -> LD_LIBRARY_PATH, /usr/lib/jvm/java-11-openjdk/lib/server:/usr/lib/jvm/java-11-openjdk/lib:/usr/lib/jvm/java-11-openjdk/../lib
2020-04-28 08:12:23.208 INFO --- [ main ] a.c.VaultConfiguration : system enviroment -> HOME, /root
2020-04-28 08:12:23.208 DEBUG --- [ main ] a.c.VaultConfiguration : property TOKEN -> ***my-vault-token***
2020-04-28 08:12:23.209 DEBUG --- [ main ] a.c.VaultConfiguration : property ENDPOINT -> ***my-vault-endpoint***
2020-04-28 08:12:23.618 DEBUG --- [ main ] a.c.VaultConfiguration : vault read -> {password=***passowrd***, url=jdbc:postgresql://***databaseurl/databasename***, username=***username***}
TestCompA
TestCompB
TestCompC
2020-04-28 08:12:24.573 INFO --- [ main ] o.h.v.i.util.Version : HV000001: Hibernate Validator 6.0.18.Final
2020-04-28 08:12:25.193 INFO --- [ main ] o.s.s.c.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-04-28 08:12:25.586 INFO --- [ main ] o.a.c.h.Http11NioProtocol : Starting ProtocolHandler ["http-nio-8080"]
2020-04-28 08:12:25.653 INFO --- [ main ] o.s.b.w.e.t.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-04-28 08:12:25.659 INFO --- [ main ] app.MainApp : Started MainApp in 8.077 seconds (JVM running for 9.391)
2020-04-28 08:12:25.662 DEBUG --- [ main ] a.r.PostgreSQLRunner : hello! PostgreSQLRunner
2020-04-28 08:12:25.663 DEBUG --- [ main ] a.r.PostgreSQLRunner : database url in property -> jdbc:postgresql://***databaseurl/databasename***
2020-04-28 08:12:25.665 DEBUG --- [ main ] a.r.PostgreSQLRunner : database username in property -> ***username***
2020-04-28 08:12:25.665 DEBUG --- [ main ] a.r.PostgreSQLRunner : database password in property -> ***passowrd***
2020-04-28 08:12:25.705 INFO --- [ main ] c.z.h.HikariDataSource : HikariPool-1 - Starting...
2020-04-28 08:12:25.942 INFO --- [ main ] c.z.h.HikariDataSource : HikariPool-1 - Start completed.
2020-04-28 08:12:25.961 DEBUG --- [ main ] a.m.T.getAllTest : ==> Preparing: select test_id ,test_data from test where 1=1
2020-04-28 08:12:26.004 DEBUG --- [ main ] a.m.T.getAllTest : ==> Parameters:
2020-04-28 08:12:26.083 DEBUG --- [ main ] a.m.T.getAllTest : <== Total: 9
2020-04-28 08:12:26.098 DEBUG --- [ main ] a.r.PostgreSQLRunner : get test -> [TestModel{testId=1, testData='data-1'}, TestModel{testId=2, testData='data-2'}, TestModel{testId=4, testData='data-2'}, TestModel{testId=3, testData='hello'}, TestModel{testId=10, testData='herdin'}, TestModel{testId=101, testData='data-101'}, TestModel{testId=1000, testData='test-1000'}, TestModel{testId=102, testData='data-102'}, TestModel{testId=1001, testData='test-1001'}]
dataSource creation ok, and connect and select my postgresql success.
Compared with using jar command, There is a differency.
jar xvf aws-codebuild-bootJar.jar
0 Mon Apr 27 08:43:36 UTC 2020 BOOT-INF/
0 Mon Apr 27 08:43:36 UTC 2020 BOOT-INF/classes/
0 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/
0 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/runner/
2792 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/runner/PostgreSQLRunner.class
0 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/controller/
632 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/controller/TestCompA.class
4220 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/controller/SampleController.class
632 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/controller/TestCompC.class
687 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/controller/TestCompB.class
0 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/dto/
0 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/dto/request/
1257 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/dto/request/UserReqeust.class
0 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/dto/model/
1403 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/dto/model/TestModel.class
0 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/service/
672 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/service/UserService.class
0 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/mapper/
531 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/mapper/TestMapper.class
0 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/config/
691 Mon Apr 27 08:43:28 UTC 2020 BOOT-INF/classes/app/config/TestConfB.class
jar xvf aws-ec2-command-build-bootJar.jar
0 Wed Apr 29 06:21:42 UTC 2020 BOOT-INF/
0 Wed Apr 29 06:21:42 UTC 2020 BOOT-INF/classes/
0 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/
812 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/MainApp.class
0 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/config/
422 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/config/ElasticsearchConfiguration.class
2821 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/config/RetryableRestTemplateConfiguration$1.class
1414 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/config/RetryableRestTemplateConfiguration.class
636 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/config/TestConfA.class
691 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/config/TestConfB.class
636 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/config/TestConfC.class
1643 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/config/VaultConfiguration$VAULT_ENVIRONMENT_KEY.class
1517 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/config/VaultConfiguration$VAULT_KEY.class
1704 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/config/VaultConfiguration$SPRING_DATABASE_PROPERTY_KEY.class
5046 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/config/VaultConfiguration.class
0 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/controller/
4220 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/controller/SampleController.class
632 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/controller/TestCompA.class
687 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/controller/TestCompB.class
632 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/controller/TestCompC.class
0 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/service/
672 Wed Apr 29 06:21:40 UTC 2020 BOOT-INF/classes/app/service/UserService.class
And that Order is same as getResources for bean definition order.