Co 3rd-party package? Když budu mít závislost třeba na commons-lang3, tak jej native-image zahrne do výsledného executable taky? To by potom: 1) narostlo, 2) zastavilo jakékoli security updates (podobný problém jako má dnes Go například).
Ostatně, to je jedna z věcí, které mi chybí u existující #!java přidané v Java 11, core Java je sice hezká na vypočítání prvočísel, ale pokud potřebuju napsat už jen JSON parsování a nějaký jednoduchý klient pro HTTP service, tak okamžitě potřebuju další package.
Závislosti se do výsledné binárky zahrnou taky. Samozřejmě jsou na ty závislosti kladená stejná omezení, jako na váš kód. Bezpečnostní updaty jsou úplně stejné, jako u kterékoli jiné Java aplikace – dodavatel vám prostě musí dodat novou verzi.
To, že je java modulární a má spoustu knihoven, a vy si vyberete jen ty knihovny, které potřebujete, je snad v pořádku, ne? A jinak Java 11 už má přímo v základní knihovně HTTP klienta, který se dá používat.
> Bezpečnostní updaty jsou úplně stejné, jako u kterékoli jiné Java aplikace – dodavatel vám prostě musí dodat novou verzi.
Když v aplikaci použiji např. /usr/share/java/postgresql.jar
místo své vlastní kopie, tak se mi ta knihovna bude aktualizovat společně s distribucí.
Zdaleka ne všechny knihovny tam budou a navíc ve verzích, které potřebuji, takže často tento přístup použít nejde, ale pro úplnost ho uvádím. Zrovna u těch JDBC ovladačů mi to přijde dobře použitelné a můžeme nechat jejich aktualizace na distribuci.
To jsem přesně myslel. Neřeším teď server side aplikace, kde má stejně větší smysl klasický JIT.
Primární cíl podle výhod uvedených v článku vidím v nahrazení současných skriptovacích jazyků používaných v distribuci, tedy perl, python atd., něčím použitelným se statickým typováním a efektivním na vývoj. Ale v takovém případě bych čekal, že budou "běžné" závislosti součásti distribuce místo, aby se tahaly s každou binary.
Tak zrovna na tohle to na 99% nikdo pouzivat nebude a hlavne nikdo nebude prepisovat perl / python skripty do Javy proto, aby to mohl kompilovat pomoci GraalVM.
Proboha, co by to melo za smysl?
Smysl GraalVM je naopak ta server side aplikace (kde se mimochodem nepouziva JIT, ale spise AoT pri startu).
Proč ne? Jestli něco ve scriptování chybí, tak je to právě staticky typovaný jazyk. Honit v runtime undefined variables v python či perl (ten je na tom ještě pořád líp kvůli povinné deklaraci) je to, co dělá vývoj extrémně neefektivní. Mít na tohle Javu, respektive rychlou Java, by na běžné skriptování vyřešilo spoustu problémů. Buď se tam dlouhodobě prosadí Java, Go nebo příšernost typu Powershell.
GraalVM je užitečný samozřejmě všude, alo AoT je na server side kontraproduktivní (pokud se po nějaké době sám nenahradí kódem z JIT). AoT na rozdíl od JIT nemá v době překladu důležité informace pro optimalizaci.
Tak zrovna nedefinovanou proměnnou v Pythonu snadno odhalí (statickou analýzou, netřeba čekat na runtime) nástroje typu Pylint, dokonce už máme i anotace typů, na tomhle se pracuje. Docela by mě zajímalo, kolik jsi napsal řádků v Pythonu, že Ti připadá neefektivní v něm psát, protože já takové zkušenosti nemám.
Pravdou ale je, že na "skriptování" se dá použít ledacos, třeba i ten Haskell nebo Lisp - důležité je cílový jazyk trochu znát a mít po ruce základní knihovny pro práci se systémem. Pár svých pracovních skriptíků, které jsem měl napsané (a tedy vymyšlené) v Pythonu, jsem si v nějaké volnější chvilce mezi normálními úkoly z legrace přepsal do Rustu, netrvalo to nijak dlouho a výsledek ani není o moc ukecanější. Uměl bych si pro podobné účely představit Julii, Go, Ruby nebo Nim, je to skoro úplně jedno.
Jo ale nedefinovaný atribut objektu už běžné pythoní nástroje neodhalí. Teď si hraju s mypy, ale pořád to není: co se zkompiluje, v tom není hloupá chyba (překlep), nebo špatné použítí typu. Navíc neexistence block-scope zanáší taky různé chyby (kdo by čekal, že mimo 'for' blok bude řídící proměnná stále nadefinovaná). Osobně jsem toho napsal v Pythonu dost na to aby mě ta jeho dynamičnost bolela. Bohužel musím v některých skriptech počítat se spuštěním Pythonu 2.5 až 2.7 a to teprve bolí. Tam kde si můžu dovolit Python 3.5 (Python 3 se neměl jmenovat Python, vždyť se změnily takové základy jako třeba operátor / ) tak tam zkouším dostat mypy, a je to o řád lepší (oproti třeba Go ale stále cca třikrát horší).
Přesně to jsem měl na mysli. V perl jsou samotné proměnné vyřešené, protože se musí povinně deklarovat, navíc je vidět jejich scope (tedy při zapnutém strict). Ale stejně jako v ostatních dynamicky typovaných jazycích, pokud volám metodu nebo šahám na atribut objektu, tak tak kontrola už tam není prakticky žádná.
Co se těch nástrojů týče, tak to vystihuje celou absurditu dynamicky typovaných jazyků - na začátku se tvrdí, že vlastně statické typování není třeba a pak se přes šílené workaroundy v komentářích či anotacích snaží ty typy dostat, aby byly přístupné aspoň pro statickou analýzu, když už ne v runtime :-/
Samozřejmě odhalí: http://pylint-messages.wikidot.com/messages:e1101 - pokud Ti to nefunguje, máš nějakou vadnou verzi Pylintu nebo něco děláš špatně. Python 2 už je legacy záležitost, jestli měl být přechod takto drsný, je věc názoru. Ale zajímá mě ten block scope - jak podle Tebe vypadá typický kód, kde to vadí? Není ta funkce/metoda moc dlouhá nebo nepoužívá se jedna proměnná/identifikátior k ukládání různých věcí? V Pythonu, jako v každém jazyce, doporučuju moc neprasit. Když má člověk trochu disciplíny a používá vhodné nástroje, spousta chyb vůbec nevznikne.
V Javě žádné běžné závislosti nejsou, s výjimkou standardní knihovny, která je součástí JRE. Jedna z výhod Javy je, že má obrovský ekosystém knihoven – z toho plyne samozřejmě i ta „nevýhoda“, že neexistují běžné závislosti. Navíc Java programátoři nejsou moc zvědaví na to, že budou používat nějakou deset let starou knihovnu jenom proto, že v nějaké rozšířené distribuci nikdo nepovažoval za nutné jí aktualizovat.
On vůbec celý tenhle koncept sdílených závislostí za běhu prostě nefunguje. Ve světě Javy se na to akorát přišlo dřív, ve světě C a C++ už se na to také přišlo nebo někde ještě právě přichází, akorát to ještě většinou není pojmenováno. Všimněte si, že vše, co v poslední době hýbe světem IT, je odklon od tohohle modelu. Nodejs sdílené závislosti za běhu nemá, Go je také nemá. Distribuce softwaru v kontejnerech má jediný cíl – odstranit závislosti za běhu a nahradit je přesně definovaným prostředím. Všimněte si, že technologie pro oddělení jmenných prostorů tady byly i dřív, ale skutečný boom kontejnerů začal teprve v okamžiku, kdy se někdo rozhodl aplikaci odizolovat i od distribučních knihoven.
Už k tomu naštěstí směřují i některé rozšířené distribuce, ne jen ty nové zaměřené na kontejnery. Od distribuce už se nebude chtít, aby poskytovala miliony balíčků, které jejich správci nestíhají aktualizovat. Naopak se od ní bude chtít, aby poskytovala stabilní a aktuální základ (jádro, správce služeb spolu s kontejnery a automatizací), a k tomu už si každý přidá přesně to, co potřebuje. Distribuce, které se neustále zaměstnávají tím, že backportují bezpečnostní patche a vyrábějí tak nové a nové verze softwaru, který nikdo netestuje a skoro nikdo nepoužívá, budou ještě pár let zajímavé pro enterprise řešení, než se i tam podaří to přirozenou výměnou nahradit. A ta doba není tak daleko – i banky, které jsou tradičně velmi konzervativní, dnes přecházejí rovnou na cloud a mikroservisy. Bude kolem toho ještě spousta trápení, někde to nezvládnou napoprvé, ale nemyslete si, že když už se k tomu rozhodly, že nakonec couvnou a řeknou, že je tohle ještě příliš nové a že couvnou o pět let zpět. Ne, to to radši budou těch pět let zkoušet znovu a znovu.
Opačný pangejt (žádné závislosti, viz kontejnery) je úplně stejně špatně. Zářným příkladem je SSL knihovna. Pokud vyvinu aplikaci a nechám ji běžet více než pět let, tak se přestane mít možnost bavit po SSL (pardon dnes se tomu říká TLS) s nejovším software. Takže je potřeba dobře oddělit to co má admin upgradovat a co může zůstat přesně ve verzi se kterou byla aplikace vyvinuta. Tam kde je šance dělat security (nebo feature) update bez zásahu vývojáře, tam se sdílené knihovny hodí. Tam, kde je potřeba nasazovat knihovnu patchnutou, nebo se stejným vývojovým cyklem jako aplikaci, tam jsou sdílené knihovny zbytečné.
Nikoli, to je právě ta mylná představa, že je možné aplikaci aktualizovat z venku, bez spolupráce autora. Nová SSL knihovna vám u pět let staré aplikace nijak nepomůže, protože ta aplikace případné nové protokoly nebude umět zapnout nebo je používat. Pokoušet se dělat security update bez zásahu vývojáře je loterie – v některých výjimečných případech to jde, ve spoustě příkladů bude bezpečnostní chyba přímo v aplikaci, a ve spoustě dalších případů bude bezpečnostní chyba ve způsobu používání knihovny, takže se opět musí opravit v aplikaci. Těžko může někdo opravdu věřit tomu, že má pět let nezáplatovanou aplikaci, ale ta aplikace je bezpečná, protože přece aplikoval bezpečnostní záplaty na knihovny.
Ve většině případů to možné je. Samozřejmě jsou díry i v aplikacích samotných, ale povětšinou ve sdílených knihovnách. Pokud je možnost upgrade globálně pro celý systém, je to mnohem efektivnější cesta. Nehledě na to, že zvláště komerční aplikace můžou být bez přístupu k jejich kódu, takže jakýkoli upgrade je komplikovaný.
Způsob, co má Java (Maven) nebo Go, není taky samospasitelný. Kromě výše uvedeného je stejně třeba poskytovat zpětnou kompatibilitu jednotlivých knihoven, bo v případě tranzitivních dependencí se jeden nebo druhý package stejně rozbije.
Aplikace či jejich závislosti si deklarují závislost na minimální verzi dané knihovny, takže případná nekompatibilita je detekována ještě v čase spuštění. Musí být zajištěna binární kompatibilita, což je o něco větší problém pro C knihovny, ještě větší pro C++, ale minimální problém pro Java s klasickým JIT. Ve chvíli, kdy upgraduju třeba z Qt3 na nekompatibilní Qt5 a mám na tom další závislost, tak se knihovna defakto stává součástí platformy, ale opět viz výše - nemůžu mít část dalších závislostí běžící s Qt3 a část s Qt5, to je problém, který existuje pořád, bez ohledu na typ balíčkování aplikace.
K tomu, aby byly díry převážně ve sdílených knihovnách, není žádný důvod. Vůbec nechápu, jak jste na něco takového přišel. Třeba chyby přetečení bufferu – to jako že aplikační programátoři umí příslušné funkce volat správně a programátoři knihoven ne? Chyby typu SQL injection budou jenom v aplikacích. Atd.
Tranzitivní závislosti nevadí, problém je, pokud na jedné knihovně závisí různé části (typicky různé knihovny nebo knihovna) a závisí na různých verzích. Jenže ona i zpětná kompatibilita má různé úrovně, a chyby se velmi často vyřeší tak, že knihovna je dál zpětně kompatibilní pro ten kód, který s chybou přímo nesouvisel. Navíc od Javy 9 jsou řešitelné i ty vícenásobné závislosti, protože modul závisí jen na modulech, které deklaruje, a závislosti jiných modulů ho neovlivňují.
Závislost jedné aplikace na Qt3 i Qt5 nemusíte jako administrátor řešit, protože ten problém vyřešil už autor té aplikace.
Ohledně bezpečnostních děr: Protože aplikace se píšou na vyšší úrovni, často ve vyšších jazycích, s použitím frameworků, které od většiny bezpečnostních chyb abstrahují. Navíc jsou poměrně časté chyby třeba v protokolech souvisejích s kryptografií, což bývá často chyba konceptu, ne kódu jako takového. Samozřejmě i na aplikační úrovni lze něco udělat špatně ("select "+...), ale s použitím běžných knihoven a good practice je to riziko řádově menší.
Ohledně tranzitivních závislostí - to je to, co jsem psal. Ohledně modulů v Java - jo, do té doby, než budu potřebovat, aby spolu ty dvě nekompatibilní verze fungovaly. Tuhle feature Java 9 považuju spíš za kontraproduktivní, bo dává falešný pocit, že tím řeší víc, než ve skutečnosti dokáže. Šedá je teorie, zelený strom života.
Ohledně Qt3 vs Qt5 - nevyřešil, protože to v principu vyřešit nejde. Ten problém existuje na obou úrovních - už v rámci vývoje a případně znovu v rámci distribuce (izolovaný container řeší potenciálně tu druhou část, se všemi zápory, které se tu diskutovaly).
Ty vyšší jazyky nejsou dobrý příklad, drtivá většina knihoven používaných v Javě je napsaná také v Javě (nebo jiném jazyce nad JVM). Navíc není pravda, že by vyšší jazyky abstrahovaly od většiny bezpečnostních chyb, naopak je to jen úzká skupina chyb. Kdyby se dalo bezpečnostním chybám jednoduše vyhnout tak, že se budou používat vyšší jazyky, dávno se v ničem jiném nepíše. Pokud je chyba v protokolu, je potřeba navrhnout nový protokol, který pak aplikace musí použít. Někdy se takové chyby dají obejít úpravou používání protokolu, což obvykle zase musí udělat aplikační programátor. Chyby konceptu jsou velice časté právě v rozhraní knihoven, kdy je možné tu knihovnu používat nebezpečným způsobem – to se opět musí opravit v aplikaci. Netuším, jak jste přišel na to, že u aplikací je to riziko řádově menší.
Pokud by spolu měly dvě nekompatibilní verze knihovny komunikovat, musí to zprostředkovávat přímo aplikace, tudíž to nebude tranzitivní závislost ale přímá závislost, a bude tam samozřejmě jen jedna verze. To samozřejmě neřeší problém, že by jedna z těch knihoven závisela na nějaké staré verzi. Ale tenhle problém má jenom jedno univerzální řešení – vykašlat se na to používat nějaké staré verze a používat všude vždy nejnovější verzi. Pak mají všichni ty závislosti stejné.
Ohledně Qt3 a Qt5 – vývojář to samozřejmě nějak vyřešil (nejspíš tak, že použije jen Qt3 nebo Qt5), protože kdyby to nevyřešil, ta aplikace vůbec nebude existovat a vy jí tudíž nemůžete někam instalovat. Navíc nikdo neodpíská vývoj aplikace jenom proto, že by se mu hodilo použít zároveň Qt3 i Qt5, ale musí si vybrat a použít jenom jednu z nich.