Легко жить с maven, когда есть доступ к центральному репозиторию, или у компании есть один корпоративный репозиторий. Все меняется, если работаешь в закрытом контуре, а количество репозиториев ближе к сотне. Под катом история о том, где искать потерявшийся артефакт и как для этого приготовить maven.

Что будет?

В статье будут примеры настройки settings, pom, описание поиска в репозиториях. Не будет настройки nexus\artifactory, проблем связанных с ними.

Начнем с простого


Добавим зависимость на spring в своем pom.

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.0.0.RELEASE</version>
</dependency>

pom.xml
<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>test</groupId>
    <artifactId>dependency-testing</artifactId>
    <version>1.0.0</version>
    <name>Dependency test</name>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.0.RELEASE</version>
        </dependency>
    </dependencies>
</project>


Предположим, что мы только установили maven и не меняли никаких настроек. После инициализации поиска зависимостей через сборку maven, или через ide, maven сначала проверит наличие артефакта в локальном репозитории в директории ${user.home}/.m2/repository/.

Если в локальном репозитории артефакта нет, то запрос пойдет в репозиторий central.


Мы не указывали этот репозиторий, но он всегда добавляется мавеном при сборке.

Сконфигурируем settings.xml

settings.xml
<?xml version="1.0" encoding="UTF-8"?>
<settings xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"
          xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <profiles>
        <profile>
            <repositories>
                <repository>
                    <id>nexus-corp</id>
                    <name>nexus-corp-repo</name>
                    <url>http://nexus.mycompany.com:8081/nexus/central/</url>
                </repository>
            </repositories>
            <id>nexus</id>
        </profile>
    </profiles>
    <activeProfiles>
        <activeProfile>nexus</activeProfile>
    </activeProfiles>
</settings>


Зачем мне settings.xml?
В нем можно настроить репозитории, зеркала, прокси, переменные среды и т.д. Прописать один раз и использовать во всех проектах (в идеальном мире)
Подробнее про settings.xml

С такой конфигурацией порядок поиска будет аналогичен варианту с конфигурацией по умолчанию, но появится дополнительный шаг. Перед тем как пойти в central, мавен попытается скачать артефакт из репозитория nexus-corp.


Зеркало


Добавим зеркало для репозитория nexus-corp.

<mirror>
    <id>nexus-corp-mirror</id>
    <name>nexus-corp-mirror</name>
    <url>http://nexus.mirror.mycompany.com:8081/nexus/central/</url>
    <mirrorOf>nexus-corp</mirrorOf>
</mirror>

Когда нужно зеркало?
  • недоступен внешний репозиторий
  • для экономии внешнего трафика
  • чтобы переопределить репозиторий из settings\pom

Подробнее про зеркала

Когда maven дойдет до шага поиска в репозитории nexus-corp, то вместо него попытается найти артефакт в репозитории nexus-corp-mirror.

При этом если он не найдет его в nexus-corp-mirror, то запроса в nexus-corp не будет.


А если добавить зеркало с wildcard, то все запросы будут направлены на него, если не указаны другие зеркала.

<mirror>
    <id>all</id>
    <name>nexus-corp-mirror</name>
    <url>http://nexus.mirror.mycompany.com:8081/nexus/central/</url>
    <mirrorOf>*</mirrorOf>
</mirror>

Порядок поиска


В общем случае схема поиска будет такой:

1. Поиск в локальном репо
2. Поиск в репозиториях в порядке объявления с учетом приоритета (либо в их зеркалах)

В конце добавляется central. Он всегда последний, если не был перезаписан.


Merge конфигураций


Перед выполнением сборки maven «склеивает» конфиги:

  • глобальный — ${maven.home}/conf/settings.xml
  • пользовательский — ${user.home}/.m2/settings.xml
  • проектный — pom.xml

С приоритетом конфигурацией у меня случился небольшой конфуз. В документации написано:
The former settings.xml are also called global settings, the latter settings.xml are referred to as user settings. If both files exists, their contents gets merged, with the user-specific settings.xml being dominant.
По факту, если при склеивании не возникло конфликтов, то приоритет репозиториев следующий, в порядке уменьшения:

  • глобальный
  • пользовательский
  • pom.xml

Из примеров ниже порядок будет следующий:

  • repo-global-setting1
  • repo-user-setting1
  • repo-user-setting2
  • repo-pom1
  • central

global-settings.xml
<?xml version="1.0" encoding="UTF-8"?>
<settings xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"
          xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <profiles>
        <profile>
            <repositories>
                <repository>
                    <id>repo-global-setting1</id>
                    <url>http://nexus.mycompany.com:8081/nexus/repo-global-setting1-url/</url>
                </repository>
            </repositories>
            <id>g-nexus</id>
        </profile>
    </profiles>
    <activeProfiles>
        <activeProfile>g-nexus</activeProfile>
    </activeProfiles>
</settings>


user-settings.xml
<?xml version="1.0" encoding="UTF-8"?>
<settings xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"
          xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <profiles>
        <profile>
            <repositories>
                <repository>
                    <id>repo-user-setting1</id>
                    <url>http://nexus.mycompany.com:8081/nexus/repo-user-setting1-url/</url>
                </repository>
                <repository>
                    <id>repo-user-setting2</id>
                    <url>http://nexus.mycompany.com:8081/nexus/repo-user-setting2-url/</url>
                </repository>
            </repositories>
            <id>nexus</id>
        </profile>
    </profiles>
    <activeProfiles>
        <activeProfile>nexus</activeProfile>
    </activeProfiles>
</settings>


pom.xml
<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>test</groupId>
    <artifactId>dependency-testing</artifactId>
    <version>1.0.0</version>
    <name>Dependency test</name>

    <repositories>
        <repository>
            <id>repo-pom1</id>
            <url>http://nexus.mycompany.com:8081/nexus/repo-pom1-url/</url>
        </repository>
    </repositories>
    
    <dependencies>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
    </dependencies>
</project>



С разрешением конфликтов появилось еще большее непонимание. Если объявить репозиторий с одним id в разных профилях, то приоритет имеет глобальный конфиг, но если объявить профили с одинаковым id, то приоритет имеет пользовательский. Дальше экспериментировать уже не стал.

Поиск пропавших


Так что делать с ошибкой Could not resolve dependencies for project myproject:jar:1.0.0: Failed to collect dependencies at com.someproject:artifact-name:jar:1.0.0?

1. Проверить правильность имени и версии артефакта.
2. Посмотреть какие репозитории\зеркала указаны в ваших settings\pom.
3. Проверить наличие артефакта в этих репозиторииях

Обычно проблема решается на втором шаге, но кроме этих пунктов встречаются проблемы из-за прокси, также бывает, что скаченный jar поврежден(хотя сам я сталкивался с таким всего один раз).

Для snapshot версий полезна команда -U — принудительное обновление snapshot зависимостей. По умолчанию maven обновляет их только после истечения таймаута раз в день(параметр updatePolicy)

В ролях


maven 3.5.0
jdk 1.8
debug логи maven

Комментарии (8)


  1. lokkersp
    12.10.2017 12:26

    Не совсем понял утверждения про Mirror

    • недоступен внешний репозиторий
    • для экономии внешнего трафика


    Не могли бы вы пояснить?
    1. В вашем случае, вы имеете 2 инстанса Nexus в своей сети, и получается что вам зеркало нужно на случай падения основного инстанса, не внешнего(Central репозитория). Или говоря «внешний», вы имеете ввиду условный Nexus/artifactory?
    Как вы экономите внешний трафик?
    2. Насколько помню, по дефолту всякие central репозитории имеют тип proxy, что значит мы получаем этакий кеширующий прокси артефактов. При переключении на зеркало зависимости выкачиваются заново, если они никак не кешированы(что вряд ли).

    Или вы держите кеш и на дополнительном инстансе?


  1. Heruvimka Автор
    12.10.2017 12:48

    1. Под недоступен внешний репозиторий я подразумевал, что недоступен, например, maven central, если разработка ведется в сети без доступа в интернет. Во внутренней сети можно поднять зеркало, которое будет проксировать запросы к central.

    Как вы экономите внешний трафик?
    Подразумевается экономия внешнего трафика. Если вы поставите зеркало для central, то артефакт будет скачан один раз и закеширован в локальном nexus, если зеркала не будет, то артефакт будет скачиваться из central при обращении каждого нового пользователя.

    2. Ответил выше?


    1. lokkersp
      12.10.2017 16:12

      2 не ответили. Artifact Cache — не фича mirror, это фича Nexus (cache proxy), вам Nexus трафик экономит, а не «зеркало».


  1. Beholder
    12.10.2017 16:08

    А можно ли сделать так, что если точно известно, что некий артефакт лежит в конкретном стороннем репозитории, то не лазить за ним в центральный и другие?


    1. lokkersp
      12.10.2017 16:31

      Если скажем вы имеете репозитории [А, В, С]- то в таком натуральном порядке и они будут опрашиваться. То есть теоретически, если вы смухлюете с порядком репозиториев, то получите свой артефакт чуть быстрее, но мне не кажется это хорошей практикой. Цель — получить в конечном счете все артефакты, а не один единственный.


      1. Beholder
        12.10.2017 21:53

        Ситуация такова, что некий продукт (библиотека, возможно даже не open-source) распространяется сторонней фирмой, либо в виде обычной загрузки, либо доступом к их собственному репозиторию. На Maven central или ещё где-либо этот артифакт, собственно, никогда не появится, и искать его там бессмысленно.


        1. lokkersp
          13.10.2017 09:16

          тот же Nexus умеет в hosted репозитории, тоже самое умеют и остальные


  1. Heruvimka Автор
    12.10.2017 22:10

    Поднять локальный репозиторий рассматривали? Проксировать запросы к стороннему репозиторию, или просто подложить руками нужный артефакт в локальный репо и установить его как прокси к центральному.
    … не в ту ветку