Jednou z novinek, které jsme přidali do WinStromu, je automatická aktualizace klientů ze serveru. Je možná trošku nepřesné nazývat tuto funkci aktualizací, protože se může snadno stát, že dojde k přesnému opaku a verze bude dokonce snížena.
WinStrom nabízí síťový přístup. Aktualizovat jednotlivé počítače ručně je sice pracné, ale proveditelné. Nicméně WinStrom nabízí také vzdálený přístup přes internet. A navštívit všechny pobočky včetně obchodních cestujících je již úkol nadlidský. Současně jsou také případy, že účetní firmy účtuje ve WinStromu pro několik zákazníků, kteří provozují WinStrom na svých serverech. Pak lze jen těžko koordinovat aktualizaci WinStromu několika zákazníku najednou. Proto by museli účetní několikrát denně zvyšovat a pak zase snižovat verze. A to se se zákazníkem na telefonu dělá opravdu špatně.
Proto jsme se rozhodli tento problém vyřešit jednou pro vždy. Při přihlášení k serveru dojde ke vzájemné kontrole čísla verze. Pokud verze neodpovídá, dojde ke stažení změněných souborů (tj. nestahuje se celý WinStrom, ale jen části) a uložení do lokální vyrovnávací paměti (cache) na disku. Pak se WinStrom ukončí a spustí správnou verzi. Při příštím spuštění se použije nejvyšší instalovaná verze. Pokud se naopak připojíte k serveru, který má verzi nižší než klient, dojde ke snížení verze. Samozřejmostí je, že pokud byla již verze jednou stažena, nestahuje se znovu.
To, že dochází ke spuštění jiné verze obvykle poznáte tak, že se po zadání jména a hesla zdánlivě WinStrom ukončí a po chvíli znovu objeví.
Díky této funkci stačí, abyste vždy aktualizovali jen server a všichni klienti se pak aktualizují automaticky.
Text, který jste dočetli až sem je zkopírován z blogu projektu WinStrom. Protože toto je blog vývojářský, zkusíme se podívat i na technickou část řešení.
Jak to celé funguje?
Celý tento systém byl inspirován programem WinBox systému Mikrotik. Řešení jsme nazvali spouštěč (Launcher). Potřebovali jsme celý systém oddělit tak, aby spouštěč fungoval i za deset let a nebylo jej potřeba aktualizovat. Proto jsme většinu kódu (např. stažení nové verze) přesunuli až do spouštěného programu, který je aktualizován.
Potřebovali jsem také zajistit komunikaci mezi spouštěčem a jednotlivými verzemi programu. Potřebujeme totiž předávat jak číslo verze, které se má spustit, tak i jméno, heslo, server a zvolený jazyk (nezapomeňte, že uživatel nejdříve zvolí server a pak teprve je možné přepnout verzi).
Abychom nemuseli řešit problémy s hledáním správného JVM, jeho spouštěním a bezpečným předáváním jména a hesla, rozhodli jsme se, že vše budeme provozovat v rámci jednoho virtuálního stroje a oddělení verzí zařídíme přes classloader.
Nakonec jsme použili knihovnu ClassWorlds na které je postaven také Maven. Jedná se o nástroj pro jednodušší práci s classpath. Umožňuje totiž složitější struktury než je jen stromová struktura. Můžete např. mít privátní a exportovanou část, dědičnost apod. V případě WinStromu tak exportujeme část pro komunikaci mezi verzemi.
Při spuštění aplikace dojde nejdříve k zavolání spouštěče. Nic jiného na classpath nenajdete. Ten najde nejvyšší verzi WinStromu, najde jeho seznam knihoven, sestaví ClassWorld a spustí funkci main. Klient se připojí na server a když zjistí, že má je špatné verze, stáhne správnou do lokální cache. K tomu použije HTTP rozhraní WinStrom serveru. Pak požádá spouštěč o spuštění správné verze a ukončí se.
Spouštěč pustí správnou verzi a předá mu údaje, které již uživatel zadal (jméno, heslo a server) a klient se rovnou přihlásí.
Vše fungovalo téměř napoprvé správně. Narazili jsme jen na jeden problém: AWT vlákno. Při prvním zavolání SWING/AWT knihovny se toto vlákno spustí a do Thread.setContextClassLoader()
nastaví aktuální classloader. Nicméně při spuštění nové verze, dojde k výměně classloaderu a začne selhávat náš kód, protože nemůže najít potřebné třídy. Proto jako první zavoláme přes invokeLater funkci, která přenastaví classloader na nově používaný. Od té doby již žádné problémy nejsou.
Celé řešení se nám osvědčilo a je součástí WinStromu již tři měsíce.
Pokud se Vám zdá použití ClassWorlds zbytečně obtížné, tak vězte, že stejné řešení je použité i na serveru. WinStrom server je totiž rozdělen na dvě části – jednoduchá služba, která nabízí WinStrom po síti a účetní server, který se spustí až podle potřeby. Tím se sníží paměťová náročnost a také zrychlí start, pokud některé služby nepoužíváte. Další technologií, kterou jsme mohli použít je OSGi. Nicméně pro naše účely byl ClassWorlds jednodušší a také menší – má 37 KiB.
Již delší dobu na firmě pozorujeme, že u Vás jedna inovace stíhá druhou. Váš přístup k problémům jaké malé firmy řeší je opravdu netradiční a v ČR nezvyklý. Plánujeme více poboček po celé ČR a aktualizace IS všude zároveň docela řešíme. Jak jste to popsal to skoro nemá chybu.
Neco podobneho jsem resil pro nase Java ME Platform SDK 3.0 a asi se shodneme na tom, ze to neni zadny med 😉 Skoro vzdy nezbude nic jineho, nez mit nejaky ostruvek stability, ktery se spatne updatuje (a kupodivu i ten nas, se jmenuje launcehr 😀 ).
Na prenaseni dat se nam osvedcil IPS (Image Packaging System), ktery je prevzaty z OpenSolarisu. Existuje i docela prijemna multiplatformni (minimalne Win, Lin, Mac, Solaris X86 a Solaris Sparc) verse.
(Btw: taky se nekdy probudite s vykrikem, kdyz vas trapi nocni mura, ve ktere potrebujete ofixovat chybu v launcheru?)
Docela by me zajimalo proc pouzivate Postgress (proc ne Derby) a jak resite jeho update.
bubak: Proč PostgreSQL jsme popsali zde http://dev-blog.ferschmann.cz/winstrom-10-pod-poklickou/
Zkráceně: Derby neumí vše, co jsme potřebovali.
PostgreSQL běží jen na serveru, který se aktualizuje normálně z balíčku (např. přes automatické aktualizace v Linuxu). Tj. potřeba vždy nainstalovat instalační balíček na serveru.
Možná to bude znít laicky, ale proč to (alespoň u Linuxu) nevyřešit přes PPA?
My používáme vlastní úložiště pro aktualizaci balíčků. To co je ale zde popisované umožňuje, abych měl současně spuštěno několik verzí podle toho na který server se přihlašuji. A někdy ani nemusím mít právo aktualizovat lokální počítač, ale do FlexiBee se chci připojit.
Snad jsem to vysvětlil.