2017-09-23 12 views
0

나는 webapp-runner을 사용하여 Heroku에서 전쟁 중입니다. 다음 명령을 사용하여 heroku-maven-plugin 버전 1.2를 사용하여 응용 프로그램을 배포합니다. mvn heroku:deploy-war. 처음에는 앱이 작동하고 모든 엔드 포인트가 유효한 응답을 반환합니다.webapp-runner에서 실행중인 Heroku web-API에 대한 API 호출은 결국 google.common에서 NoSuchMethodError, NoClassDefFoundError와 함께 실패합니다.

2017-09-23T19:19:45.388865+00:00 app[web.1]: SEVERE: Servlet.service() for servlet [jersey-serlvet] in context with path [] threw exception [org.glassfish.jersey.server.ContainerException: java.lang.NoSuchMethodError: com.google.common.base.CharMatcher.ascii()Lcom/google/common/base/CharMatcher;] with root cause 
2017-09-23T19:19:45.388866+00:00 app[web.1]: java.lang.NoSuchMethodError: com.google.common.base.CharMatcher.ascii()Lcom/google/common/base/CharMatcher; 
2017-09-23T19:19:45.388867+00:00 app[web.1]: at com.google.common.io.BaseEncoding$Alphabet.<init>(BaseEncoding.java:453) 
2017-09-23T19:19:45.388868+00:00 app[web.1]: at com.google.common.io.BaseEncoding$Base64Encoding.<init>(BaseEncoding.java:892) 
2017-09-23T19:19:45.388869+00:00 app[web.1]: at com.google.common.io.BaseEncoding.<clinit>(BaseEncoding.java:317) 
...application specific stack trace 

같은 API에 대한 모든 후속 호출은 NoClassDefFoundError에서 생산 : I 앱이 충분히 Heroku가 잠을 다음 구아바를 호출 엔드 포인트를 호출 넣어 공회전 할 수 있도록 그러나, 나는이 NoSuchMethodError받을 같은 점

2017-09-23T19:22:24.454901+00:00 app[web.1]: SEVERE: Servlet.service() for servlet [jersey-serlvet] in context with path [] threw exception [org.glassfish.jersey.server.ContainerException: java.lang.NoClassDefFoundError: Could not initialize class com.google.common.io.BaseEncoding] with root cause 
2017-09-23T19:22:24.454903+00:00 app[web.1]: java.lang.NoClassDefFoundError: Could not initialize class com.google.common.io.BaseEncoding 
...application specific stack trace 

이러한 문제는 구아바 항아리가 컴파일 타임에 존재하지만 런타임에는 나타나지 않는 것으로 보입니다. 그러나, 나는 웹 다이나모에-로그인과 구아바 항아리가 나는 엔드 포인트가 응용 프로그램이 배포 된 후 즉시 작동하지만 나중에 이러한 오류가 발생 이유를 설명하기 위해 고군분투 내 warfile

my-mbp:TrickServer me$ heroku ps:exec 
Establishing credentials... done 
Connecting to web.1 on ⬢ myapp... 
~ $ cd target/ 
~/target $ ls 
MyApp.war dependency mvn-dependency-list.log tomcat.52079 
~/target $ jar -tf MyApp.war 
...lots of dependencies... 
WEB-INF/lib/google-oauth-client-1.20.0.jar 
WEB-INF/lib/gson-2.2.4.jar 
WEB-INF/lib/guava-23.0.jar  <---guava 
WEB-INF/lib/guava-jdk5-13.0.jar 
...lots more dependencies... 

에 포함 된 것을 확인. 나에게이 동작은 Heroku가 처음 실행되거나 Heroku가 이동 중이거나 구아바 jarfile을 정리할 때보 다 잠자기에서 깨어날 때 다른 클래스 패스를 잠재적으로 제공하고 있음을 제안하는 것으로 보인다. 내 Procfile

내용 :

web: java $JAVA_OPTS -jar target/dependency/webapp-runner.jar --port $PORT --expand-war target/MyApp.war 

자바 내 웹 다이노에서 실행 해 처리합니다

~/target $ ps -ef | grep java 
u30439  4  1 0 18:50 ?  00:00:44 java -Xmx300m -Xss512k -Dfile.encoding=UTF-8 -Duser.timezone=UTC -jar target/dependency/webapp-runner.jar --port 52079 target/MyApp.war 
u30439  27  4 0 18:50 ?  00:00:00 bash --login -c java $JAVA_OPTS -jar target/dependency/webapp-runner.jar $WEBAPP_RUNNER_OPTS --port 52079 target/MyApp.war 

업데이트 나는 --expand-war 인수 나는 내 웹 애플리케이션을 호출하고 이후 1

또한 확장 된 디렉토리의 jar 파일을 검사하여 구아바가 있는지 확인했습니다. 그것은이다 :

~/target/tomcat.55320/webapps/expanded/WEB-INF/lib $ ls 
...dependencies... 
google-oauth-client-1.20.0.jar 
gson-2.2.4.jar 
guava-23.0.jar 
guava-jdk5-13.0.jar 
...more dependencies... 

이 내가 클래스 경로를 인쇄 출력하는 문제가 웹 서비스에 다음과 같은 논리를 추가 업데이트 그것에 자원 :

logger.info("System Classpath: " + System.getProperty("java.class.path")); 
logger.info("Runtime Classes..."); 
    ClassLoader cl = UserService.class.getClassLoader(); 
    URL[] urls = ((URLClassLoader) cl).getURLs(); 
    for(URL url: urls){ 
     logger.info(url.getFile()); 
    } 

다음에 오류 내가 로그를 조사한 결과 놀랍게도 구아바 항아리가 런타임 classpath에 있다는 것을 발견했습니다!

2017-09-24T12:07:40.843438+00:00 app[web.1]: [heroku-exec] ERROR: Could not connect to proxy: 
2017-09-24T12:07:40.844145+00:00 app[web.1]: [heroku-exec] ERROR: Too many reconnect attempts. Waiting 30 seconds... 
2017-09-24T12:07:52.671620+00:00 app[web.1]: Sep 24, 2017 12:07:52 PM org.myorg.server.web.services.MyService authenticate 
2017-09-24T12:07:52.671631+00:00 app[web.1]: INFO: System Classpath: target/dependency/webapp-runner.jar 
2017-09-24T12:07:52.671931+00:00 app[web.1]: Sep 24, 2017 12:07:52 PM org.myorg.server.web.services.MyService authenticate 
2017-09-24T12:07:52.671932+00:00 app[web.1]: INFO: Runtime Classes... 
2017-09-24T12:07:52.672277+00:00 app[web.1]: Sep 24, 2017 12:07:52 PM org.myorg.server.web.services.MyService authenticate 
2017-09-24T12:07:52.672279+00:00 app[web.1]: INFO: /app/target/tomcat.28304/webapps/expanded/WEB-INF/classes/ 
.... 
2017-09-24T12:07:52.690304+00:00 app[web.1]: Sep 24, 2017 12:07:52 PM org.myorg.server.web.services.MyService authenticate 
2017-09-24T12:07:52.690306+00:00 app[web.1]: INFO: /app/target/tomcat.28304/webapps/expanded/WEB-INF/lib/google-oauth-client-1.20.0.jar 
2017-09-24T12:07:52.690501+00:00 app[web.1]: Sep 24, 2017 12:07:52 PM org.myorg.server.web.services.MyService authenticate 
2017-09-24T12:07:52.690503+00:00 app[web.1]: INFO: /app/target/tomcat.28304/webapps/expanded/WEB-INF/lib/guava-23.0.jar <--- Guava!!! 
.... 

여기에 무슨 일이 일어나고 있습니까? 어떻게 디버깅합니까?

답변

0

를 참조하십시오, 내 프로그램은 클래스 경로 (guava-23.0.jar & guava-jdk5-13.0.jar)에 구아바의 두 가지 버전을 가지고 있음을 발견했다. 디버깅 팁에서 here이 필요하지만 충분하지는 않았다고 제안했습니다.

ClassLoader로 작업 할 때 .class 객체에 정의 된 getClassLoader 메서드는 원래 클래스를로드 한 ClassLoader에 대한 참조를 반환한다는 것을 기억해야합니다. 중복 항아리를 찾으려면 NoSuchMethodError으로 실패한 클래스를로드 한 동일한 ClassLoader에서 classLoader.getResource("/com/google/common/base/CharMatcher.class")을 호출하는 것이 중요했습니다.

후손을 위해 충돌을 일으킨 특정 종속성은 com.google.api-client입니다. 종속성에 다음을 추가하여이 문제를 해결했습니다. pom.xml

<dependency> 
    <groupId>com.google.api-client</groupId> 
    <artifactId>google-api-client</artifactId> 
    <version>1.22.0</version> 
    <exclusions> 
     <exclusion> 
      <groupId>com.google.guava</groupId> 
      <artifactId>guava-jdk5</artifactId> 
     </exclusion> 
    </exclusions> 
</dependency>