[springboot] 2.0 을 사용하면서 패키징과 관련된 문제가 발생한다면

스프링 부트 2.0 을 사용하면서 패키징과 관련된 찝찝한 느낌을 받고 있는 요즘이다.

그 주요 원인은 스프링 부트 그레이들 플러그인 2.0 때문이다. 스프링 부트 그레이들 플러그인 2.0이 되면서 기존에 있던 bootRepackagebootJarbootWar 으로 변경(Spring Boot’s new Gradle plugin)되면서 부터다.

실행가능한 jar와 war를 만드는데 사용했던 bootRepackage를 배포파일에 따라서 각각 JarbootJarWarbootWar로 확장하는 과정에서 이런 상황이 발생했다.

핵심요점만 이야기하자면 bootJarbootWar를 비활성화하면 패키징 태스크가 실행되지 않는다.

//bootJar 비활성화시
bootJar.enabled = false
jar.enabled = true

//bootWar 비활성화시
bootWar.enabled = false
war.enabled = true

위와 같은 형태로 상위 태스크를 활성화시켜줘야지만 패키징 태스크가 실행된다. 이러한 사실을 알게된 것은

가 있었고, 오늘은 EBS(AWS Elastic Beanstalk)에서 "구성 파일(.ebextensions)을 사용하여 고급 환경 사용자 지정" 기능을 사용하기 위해서 프로젝트 내에 .ebextensions 디렉터리를 war 파일 패키징시 이동하기 위해 그레이들내에서 다음과 같이 선언했는데 .ebextensions 디렉터리가 패키징에서 누락된 것을 발견했다.

build.gradle
project(":api-module") {
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'
    apply plugin: 'war'

    war {
        from('./src/main/ebextensions') {
            into('.ebextensions')
        }
    }

    dependencies {
        compile project(":core-module")

        compile('org.springframework.boot:spring-boot-starter-data-rest')
        compile('org.springframework.boot:spring-boot-starter-hateoas')
        compile('org.springframework.boot:spring-boot-starter-web-services')
        compile('org.springframework.boot:spring-boot-starter-webflux')
        compile('org.springframework.data:spring-data-rest-hal-browser')

        compileOnly "org.springframework.boot:spring-boot-configuration-processor"

        testCompile('org.springframework.boot:spring-boot-starter-test')
        testCompile('io.projectreactor:reactor-test')
    }
}

위와 같이 모듈을 정의했는데, war 태스크를 정의한 부분이 무시됐다. (이런!!)

하지만 그렇다고 분노하거나 노여워하지 말자(그건 내가 다 했으니…​).

여기서 대응할 수 있는 방법은 크게 2가지가 있다.

  • 새로추가된 booWar 태스크를 사용하는 경우

  • 기존 war 태스크를 유지하는 경우

Note

스프링 부트를 기반으로 할 때는 실행가능하게 패키징(Repackaging)을 하는 bootJarbootWar를 사용한다고 생각하면 마음의 평화가 찾아올 듯 하다.

추천방법: 새로 추가된 booWar 태스크를 사용하는 경우

project(":api-module") {
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'
    apply plugin: 'war'

    bootWar {
        from('./src/main/ebextensions') {
            into('.ebextensions')
        }
    }

    dependencies {
        compile project(":core-module")

        compile('org.springframework.boot:spring-boot-starter-data-rest')
        compile('org.springframework.boot:spring-boot-starter-hateoas')
        compile('org.springframework.boot:spring-boot-starter-web-services')
        compile('org.springframework.boot:spring-boot-starter-webflux')
        compile('org.springframework.data:spring-data-rest-hal-browser')

        compileOnly "org.springframework.boot:spring-boot-configuration-processor"

        testCompile('org.springframework.boot:spring-boot-starter-test')
        testCompile('io.projectreactor:reactor-test')
    }
}

기존 war 태스크를 유지하는 경우

Note

톰캣과 같은 WAS에 war 파일을 배포한다면 실행가능하게 패키징을 필요로 하지 않으니 bootWar를 비활성화 하자.

project(":api-module") {
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'
    apply plugin: 'war'

    bootWar.enabled = false // (1)
    war.enabled = true  // (2)

    war {
        from('./src/main/ebextensions') {
            into('.ebextensions')
        }
    }

    dependencies {
        compile project(":core-module")

        compile('org.springframework.boot:spring-boot-starter-data-rest')
        compile('org.springframework.boot:spring-boot-starter-hateoas')
        compile('org.springframework.boot:spring-boot-starter-web-services')
        compile('org.springframework.boot:spring-boot-starter-webflux')
        compile('org.springframework.data:spring-data-rest-hal-browser')

        compileOnly "org.springframework.boot:spring-boot-configuration-processor"

        testCompile('org.springframework.boot:spring-boot-starter-test')
        testCompile('io.projectreactor:reactor-test')
    }
}
  1. bootWar를 비활성화한다.

  2. war를 활성화한다.

정리

  • 기술을 사용할 때는 참고문서를 보자.

참고문헌

[스프링부트] Executable JAR 내에서 ResourceUtil.getFile() 사용시 FileNotFoundException 발생

발생문제

IDE에서 실행하거나 bootrun 명령으로 실행시켰을 경우에는 별다른 이상없이 동작하던

File defaultProfileImage = ResourceUtil.getFile("static/images/default-profile.png");

가 실행가능한 jar(Excutable jar) 형태로 만들어 실행시에는 java.io.FileNotFoundException 을 일으킨다. 그 접근경로를 살펴보면,

...Exception: java.io.FileNotFoundException: class path resource [static/images/default-profile.png] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/home/ihoneymon/.../build/libs/innoquartz-server.war!/WEB-INF/classes!/static/images/default-profile.png
    at io.honeymon.test.member.ApplicantServiceImpl.setDefaultProfileImage(ApplicantServiceImpl.java:59) ~[classes!/:na]
    at io.honeymon.test.member.ApplicantServiceImpl.register(ApplicantServiceImpl.java:39) ~[classes!/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_25]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_25]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_25]
    at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_25]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) ~[spring-aop-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281) ~[spring-tx-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at com.sun.proxy.$Proxy172.register(Unknown Source) ~[na:na]
    at io.honeymon.test.member.SignupController.signUp(SignupController.java:53) ~[classes!/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_25]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_25]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_25]
    at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_25]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:222) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:814) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:737) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:969) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:871) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:707) [javax.servlet-api-3.1.0.jar!/:3.1.0]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:845) [spring-webmvc-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) [javax.servlet-api-3.1.0.jar!/:3.1.0]
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:812) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1669) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:224) [websocket-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration$ApplicationContextHeaderFilter.doFilterInternal(EndpointWebMvcAutoConfiguration.java:243) [spring-boot-actuator-1.3.1.RELEASE.jar!/:1.3.1.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:111) [spring-boot-actuator-1.3.1.RELEASE.jar!/:1.3.1.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:316) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:126) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:122) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:48) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:158) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:205) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176) [spring-security-web-4.0.3.RELEASE.jar!/:4.0.3.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:103) [spring-boot-actuator-1.3.1.RELEASE.jar!/:1.3.1.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.4.RELEASE.jar!/:4.2.4.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:585) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:577) [jetty-security-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1127) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515) [jetty-servlet-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.Server.handle(Server.java:499) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:311) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:257) [jetty-server-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:544) [jetty-io-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635) [jetty-util-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555) [jetty-util-9.2.14.v20151106.jar!/:9.2.14.v20151106]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_25]

로그를 살펴보면 cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/home/ihoneymon/.../build/libs/innoquartz-server.war!/WEB-INF/classes!/static/images/default-profile.png 항목이 보일 것이다. 이에 대해서는,

해결책

war 파일이나 IDE로 application run as로 실행하였다면 실제 resource 파일인 file:// 프로토콜을 쓰기 때문에 File객체를 생성해 줄수 있지만, executable jar로 실행 했다면 FileNotFoundException이 발생 하게 됩니다.

에서 설명하고 있다. jar 파일로 실행시에는 VfsResourceDelegate 를 사용하면서 실제 파일시스템에서 지원하는 프로토콜로 처리가 되기에 의도했던 것과는 다르게 동작하게 된다. 이를 해결하기 위한 방법도 위의 포스팅에서 설명되어 있다.

해결적용

ClassPathResource classPathResource = new ClassPathResource("static/images/default-profile.png");
// classPathResource.getInputStream(); 를 사용하도록 처리 

문제 해결!

참고



실행가능한 JAR(executable jar)을 생성하게 되면 다음과 같이 META-INF 폴더 밑에 MANIFEST.MF 파일이 생성된다.

├── Application.class
└── META-INF
    └── MANIFEST.MF

MANIFEST.MF 내용

  • Manifest specification
    Manifest-Version: 1.0
    Class-Path: .
    Main-Class: Application // 실행가능한jar의 엔트리포인트가 되는 클래스명
    

예제

  • java.util.jar.JarFile API를 이용해서 Jar 파일에 대한 정보를 읽어드릴 수 있다.
  • 다음과 같이 JDK6과 JDK7에서 가져올 수 있는 부분이 다른 것으로 보인다.

    ㅡ_-);; JDK6, JDK7, JDK8에서 다를 수 있는데 그걸 테스트하기가 좀… 귀찮…

JarFile jarFile = new JarFile(/** 식별하려고 하는 jar 파일*/);

private boolean hasMainClassManifest(JarFile jarFile) throws IOException {
    Double javaVersion = Double.parseDouble(System.getProperty("java.specification.version"));
    log.debug("Java version: {}", javaVersion);
    log.debug("Has Main-Class: {}", jarFile.getManifest().getEntries().containsKey("Main-Class"));
    log.debug("Has Main-Class: {}", jarFile.getManifest().getMainAttributes().getValue("Main-Class"));
    if(1.7 > javaVersion) {
        return jarFile.getManifest().getEntries().containsKey("Main-Class");
    } else if(1.7 == javaVersion) {
        return null != jarFile.getManifest().getMainAttributes().getValue("Main-Class");
    } else {
        //TODO JDK 8 에서는 어떻게 될까?
        log.debug("Not implements");
        return false;
    }
}

대략 위의 메서드를 통해서 선택한 jar가 실행가능한 Main-Class를 가지고 있는지 여부를 확인할 수 있다.

참고


방법은 간단하다.
java -jar 파일명.jar
하면 된다.

+ Recent posts