Maven 빌드 할 때 PKIX path building failed 해결 방법

필자가 이클립스 Luna에서 Neon으로 넘어오면서 갑자기 빌드가 안된다거나(https://blog.silentsoft.org/archives/19), 이번 포스팅의 주제인 PKIX 에러가 난다거나 하는 등의 난감한 상황들을 겪었다. 그럴 때마다, 또 앞으로 벌어지는 난감한 상황들에 대해 포스팅을 쓸 테지만, 누군가에게 꼭 도움이 되었으면 좋겠다.

Maven 빌드 할 때 아래와 같이 PKIX 에러가 나는 경우가 있다.

from/to central (https://repo.maven.apache.org/maven2): sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

사용하는 디펜던시가 https 레파지토리에 있다거나, 빌드 할 때 사용되는 Maven 플러그인을 다운로드할 때 https로 요청하는 경우, 정상적으로 연결이 되지 않으면 발생하는 에러이다.

구글링해보면 돌아다니는 InstallCert.java를 다운로드해서 컴파일하고 실행하는 등의 인증서 추가를 통한 해결 방법이 나오는데, SSL 통신이 아닌 Maven 빌드 할 때 발생하는 PKIX 에러는 아래와 같은 방법으로 근본적인 문제를 해결할 수 있다.

해결 방법은 매우 간단한데, pom.xml에서 https 프로토콜을 http로 바꿔주기만 하면 해결된다.

이클립스에서 pom.xml 파일을 열고, Effective POM 탭을 열어보면 아래와 같은 설정 정보가 보인다.

...
  <repositories>
    <repository>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
      <id>central</id>
      <name>Central Repository</name>
      <url>https://repo.maven.apache.org/maven2</url>
    </repository>
  </repositories>
  <pluginRepositories>
    <pluginRepository>
      <releases>
        <updatePolicy>never</updatePolicy>
      </releases>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
      <id>central</id>
      <name>Central Repository</name>
      <url>https://repo.maven.apache.org/maven2</url>
    </pluginRepository>
  </pluginRepositories>
...

이것을 그대로 복사하여 pom.xml 탭에서 https가 아닌 http로 재정의해주면 된다.

<project
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

...
 
  <repositories>
    <repository>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
      <id>central</id>
      <name>Central Repository</name>
      <url>http://repo.maven.apache.org/maven2</url>
    </repository>
  </repositories>
  <pluginRepositories>
    <pluginRepository>
      <releases>
        <updatePolicy>never</updatePolicy>
      </releases>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
      <id>central</id>
      <name>Central Repository</name>
      <url>http://repo.maven.apache.org/maven2</url>
    </pluginRepository>
  </pluginRepositories> 

  <properties>
    ...
  </properties>

...

Maven 빌드 할 때 라이브러리를 못찾는 경우 해결 방법

Maven 빌드 할 때 “package does not exist” 또는 “cannot find symbol”과 같은 에러는 라이브러리를 찾지 못해서 나는 에러다.

이런 에러는 아래와 같이 크게 두 가지로 나눌 수 있다.

1. 레파지토리에 배포된 라이브러리를 사용하는 경우

pom.xml에 해당 라이브러리 dependency를 아래와 같이 추가하면 된다.

예)

<dependencies>
    <!-- Web dependencies -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.0.1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
        <scope>provided</scope>
    </dependency>
    ...
</dependencies>
2. 외부 라이브러리(3rd party) jar 파일을 사용하는 경우

필자는 이클립스 Luna를 쓰다가 얼마전에 공개된 Neon으로 업데이트 했는데, 업데이트하고 나서 Maven 컴파일 에러때문에 굉장히 당황했다. (당장 급하게 배포해야하는 상황인데, 컴파일이 안되니.. 쩝…) 어찌되었건, 필자는 스프링 프로젝트에 (레파지토리에 배포되지 않은) 외부 라이브러리를 src/main/webapp/WEB-INF/lib 경로에 두는데, 이 jar 파일들을 빌드할 때 참조될 수 있도록 수동으로 dependency를 주입하면 된다.

<properties>
    ...
    <webapp.lib>${basedir}/src/main/webapp/WEB-INF/lib</webapp.lib>
</properties>

<dependencies>
    ...
    <dependency>
        <groupId>whatever</groupId>        <!-- Doesn't matter -->
        <artifactId>example</artifactId>   <!-- Doesn't matter -->
        <version>whatever</version>        <!-- Doesn't matter -->
        <scope>system</scope>
        <systemPath>${webapp.lib}/example-1.0.0.jar</systemPath>
    </dependency>
</dependencies>

(groupId, artifactId, version은 jar 파일과 무관하게 정의해도 상관없다.)

의존성 주입이 안되는 (레파지토리에 배포되지 않은) 외부 라이브러리는 이렇게 해야만 Maven 컴파일 에러가 안나는데, 필자가 개발하고 있는 프로젝트의 외부 jar가 60여개가 넘어가서 도저히손으로는 dependency 태그를 만들 엄두가 안났다. (사실 이클립스 Luna는 이렇게까지 하지 않아도 에러 안났다. 업그레이드 한 Neon이 문제..)

그래서, 그냥 … 코드로 만들었다.

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.Test;

public class DependencyGenerator {

    @Test
    public void generate() throws Exception {
        AtomicInteger dependencyCount = new AtomicInteger(0);
        Files.walkFileTree(Paths.get(System.getProperty("user.dir"), "src", "main", "webapp", "WEB-INF", "lib"), new FileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                String fileName = file.getFileName().toString();

                System.out.println("        <dependency>");
                System.out.println("            <groupId>whatever</groupId>");
                System.out.println(String.format("%s%s%s", "            <artifactId>", fileName.substring(0, fileName.lastIndexOf(".")), "</artifactId>"));
                System.out.println("            <version>whatever</version>");
                System.out.println("            <scope>system</scope>");
                System.out.println(String.format("%s%s%s", "            <systemPath>${webapp.lib}/", fileName, "</systemPath>"));
                System.out.println("        </dependency>");

                dependencyCount.incrementAndGet();

                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                throw new IOException("visitFileFailed");
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                return FileVisitResult.CONTINUE;
            }
        });
        System.out.println(String.format("%s%s%s", ">> Generated ", dependencyCount.get(), " dependencies."));
    }
    
}

여기서 중요한건, groupId와 artifactId와 version이 전부다 통일되서 같은 의존성이 여러개 생기면 또 컴파일 안된다.

그래서 groupId와 version은 그냥 “whatever”로 정의하고, artifactId만 파일명을 따라가도록 했다.

위에서 잠깐 언급한대로이클립스 Luna를 사용할 때에는 이런류의 컴파일 에러는 나지 않았다. 난다고 하더라도, Alt + F5 후에 .classpath 파일에 생기는아래 라인을 제거하면 쉽사리 해결됐었다.

<classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/>

왜인지 모르겠지만, Neon은 .classpath 파일에서 위 라인을 지워도 해결이 안된다. 더 좋은 해결책을 찾던가, 아니면.. Neon부터는 이렇게 할 수 밖에…

Pagination


© 2017. All rights reserved.

Powered by Hydejack v7.5.1