úterý 27. listopadu 2007

Závislosti v Mavenu

Závislosti jsou jednou z vlastností, kterou na Mavenu oceňuji. Bohužel závislosti, především ty tranzitivní, mají i některé nevýhody. Pro neznalé Mavenu, tranzitivní závislosti jsou ty závislosti, na kterých váš kód závisí nepřímo. Příklad to osvětlí, máme komponentu A, která závisí na komponentě B a ta na C. Z pohledu A, je B přímá závislost a C nepřímá závislost.

Možná si říkáte, kde je problém. Problém nastane ve chvíli kdy se vám začnou do projektu tranzitivně dostávat závislosti, které nejsou potřeba (nechtěné) a nebo dojde ke konfliktu verzí. Klasickým příkladem je Commons Logging 1.1. Pokud se rozhodnete, že váš kód bude pro logování používat Commons Logging a zadefinujete tuto závislost, máte rázem problém. Problém se jmenuje nechtěná závislost na Servlet API 2.3, kterou tam zanese Commons Logging. Commons Logging má tuto závislost špatně nadefinovanou resp. je tam špatný scope. Podle scope se Maven rozhodne v jakých classpath je závislost potřeba. Pokud není uveden scope, je závislost propagována do všech classpath. To má pro vás ten důsledek, že se Servlet API 2.3 dostane například do výsledného packagingu což může být WAR, kde jistě nemá co dělat.

Dalším problém mohou být licence. Tranzitivní závislosti mohou zavléct do výsledného produktu licence, které nejsou úplně kompatibilní s licenční politikou produktu. No a aby těch možných konfliktů nebylo málo, tak si představme situaci kdy máme komponentu A, která závisí na komponentě X a Y. Jak X tak Y závisí na Commos Logging nicméně každý definuje jinou verzi této knihovny.

Pro ovlivnění tranzitivní závislosti máme pouze omezený výčet možností. Můžeme použít exclusion, kterým lze definovat, která tranzitivní závislost se má ignorovat. Další možností je využít znalosti Maven algoritmu pro řešení konfliktu verzí, takzvanou nejkratší cestu. Tím můžeme určit společnou verzi pro X a Y, a to tak že A bude záviset na konkrétní verzi Commons-Loggig. Bohužel všechny tyto možnosti nejsou příliš pohodlné pokud to musíme dělat pro více závislostí. Navíc je potřeba neustále brát na zřetel fakt, že jakákoliv závislost může zavléct něco nechtěného.

Když se nad tím zamyslím, tak tyto problémy nejsou chybou Mavenu, ale důsledkem špatné definice závislostí. Maven s tím těžko může něco dělat, kromě toho že poskytne prostředky pro jejich odhalení. Každopádně je tu problém, který je potřeba řešit. Jedině možné řešení je podle mého názoru vzít si závislosti kompletně pod svou kontrolu a to znamená:

  • Odpojení interní artefakt repository od internetu. Tím se nám do repository nedostane žádná závislost, kterou tam vlastnoručně nedáme. Bohužel tím taky odstřihneme Maven od nejnovějších pluginů, které jsou rovněž získávaný přes repository.
  • Post zpracování všech POM souborů v repository. To je práce, která může být pouze částečně zautomatizovaná. Nechtěné závislosti musí být ovšem vyhledány a opraveny ručně. Pro odstranění kolize verzí je možné využít takzvaný management závislostí. To znamená, že u všech tranzitivních závislostí se odstraní jejich verze případně se nahradí proměnnou. Její hodnota se nadefinuje v hlavním projektovém POM souboru, případně v profilu.

Když to vezmu a kolem, tak je s podivem, že při oblíbenosti Mavenu nevznikají komerční repository, které tyto služby profesionálně nenabízejí. Ty by mohly garantovat jednak konzistenci (verze, licence) daných stromů závislostí a jednak jejich původ - podepsané JARy. Navíc by zajistily to, že jsou k dispozici zdrojové soubory a javadoc závislostí. Další pěknou vlastností by byla možnost verzování repository. To znamená mít možnost různého stavu repository v čase a možnost se k nim vracet. Když vám dnes někdo prodává infrastrukturu jako je verzovací sýstém a nebo bug tracking systém či wiki, tak proč by nemohl nabídnout i artefakt repository.