Práce s externími Java archivy v programovacím jazyku Clojure

Pavel Tišnovský 13. 9. 2016

Při práci s projekty psanými v Clojure se používá nástroj Leiningen, s jehož možnostmi jsme se již seznámili. Dnes si ukážeme řešení jednoho problému z praxe – přidání Java archivů do projektu.

Obsah

1. Práce s externími Java archivy v programovacím jazyku Clojure

2. První demonstrační příklad – malé zopakování: vložení přeložených tříd do projektu

3. Vytvoření a otestování (uber)archivu s projektem

4. Druhý demonstrační příklad – přidání Java archivu do podadresáře resources

5. Přímé spuštění projektu nástrojem Leiningen a otestování nastavení CLASSPATH

6. Vytvoření a otestování (uber)archivu s projektem

7. Korektní řešení – použití lokálního Maven repositáře

8. Pomocný projekt určený pro vložení externího Java archivu do lokálního Maven repositáře

9. Vygenerování artefaktu pro lokální Maven repositář

10. Vytvořený artefakt

11. Třetí demonstrační příklad – přidání závislosti do našeho projektu

12. Vytvoření a otestování (uber)archivu s projektem

13. Odkazy na předchozí části tohoto seriálu

14. Odkazy na Internetu

1. Práce s externími Java archivy v programovacím jazyku Clojure

V úvodních částech seriálu o programovacím jazyce Clojure jsme se seznámili s některými možnostmi, které vývojářům nabízí nástroj Leiningen. Připomeňme si jen ve stručnosti, že se jedná o multiplatformního správce projektů, který je rozšiřitelný pomocí takzvaných pluginů a který interně používá Maven, čehož si však mnozí vývojáři ani nemusí všimnout. Mezi základní úlohy, které nástroj Leiningen vývojářům nabízí, patří:

  1. Vytvoření nového projektu na základě vybrané a předem připravené šablony. K dispozici jsou šablony běžné aplikace (doplněné o jednotkové testy), přídavného modulu pro samotný Leiningen atd. Základním příkazem je lein new.
  2. Automatické stažení všech knihoven a jejich závislostí na základě konfigurace zapsané do souboru project.clj. Tuto funkci zajistí příkaz lein deps.
  3. Spuštění projektu s možností předání parametrů příkazového řádku. Příkaz je v tomto případě jednoduchý: lein run.
  4. Spuštění jednotkových testů, které mohou být vytvořeny společně s projektem (kostra jednoho testu je připravena automaticky). Snadno uhádnutelný příkaz, který testy spustí, se jmenuje lein test.
  5. Relativně rychlá kontrola syntaxe zdrojových kódů i kontrola existence volaných metod (lein check).
  6. Lokální instalace projektu do specifikovaného adresáře (lein install).
  7. Příprava Java archivu (souboru s koncovkou .jar) takovým způsobem, aby bylo možné aplikaci nasadit i na jiném počítači. V rámci přípravy archivu se provádí překlad vybraných modulů do Javovského bajtkódu (lein jar).
  8. Příprava Java archivu obsahujícího i všechny závislé knihovny včetně samotného Clojure. Takto vytvořený „uberjar“ je posléze možné nasadit na jakémkoli počítači vybaveném pouze JRE (běhovým prostředím jazyka Java). Zajistí příkaz lein uberjar. Právě některými vlastnostmi tohoto příkazu se dnes budeme zabývat.
  9. Spuštění smyčky REPL s nastavením cest ke všem potřebným knihovnám a modulům (lein repl).
  10. Diagnostické příkazy, z nichž dnes využijeme především příkazlein classpath určený pro výpis aktuálně nastaveného seznamu cest k třídám a dalším zdrojům.
  11. Každý příkaz Leiningenu je v rozumné míře popsán v nápovědě (lein help příkaz).

2. První demonstrační příklad – malé zopakování: vložení přeložených tříd do projektu

U většiny projektů, které jsou psány v programovacím jazyce Clojure se nevyhneme nutnosti vhodným způsobem volat metody nějakých tříd vytvořených v Javě a přeložených do bajtkódu, tedy do souborů .class. Samotné vytváření (konstrukce) objektů, volání jejich metod či přístup k atributům je v programovacím jazyce Clojure poměrně jednoduchý, protože lze využít takzvané java interop. Co je ovšem neméně důležité – interpret a překladač jazyka Clojure běží nad virtuálním strojem Javy (JVM), který musí být informován o tom, na kterém místě či místech hledat třídy přeložené do bajtkódu (jedná se o CLASSPATH, s níž se již pravděpodobně všichni vývojáři používající Javu setkali). V případě, že se pro správu projektů psaných v Clojure používá nástroj Leiningen, je nastavení CLASSPATH ponecháno právě tomuto nástroji, což s sebou může přinášet některé problémy, které souvisejí s použitím externích Java archivů. Musíme být totiž schopni:

  1. Spustit interaktivní smyčku REPL a přitom mít k dispozici všechny potřebné třídy a knihovny.
  2. Vytvořit JAR archiv bez dalších závislostí, ty se přidají na CLASSPATH.
  3. Vytvořit „uberjar“ obsahující naprosto všechny závislosti (takový archiv vyžaduje ke svému běhu pouze JVM, nic jiného).

Podívejme se nejdříve na zjednodušený příklad, v němž budeme potřebovat volat javovskou metodu uloženou v bajtkódu (tedy v souboru typu class). Nejdříve vytvoříme kostru nového projektu:

lein new app external-jar-1

Vytvořená kostra projektu je tvořena standardizovanou adresářovou strukturou:

tree
 
.
├── doc
│   └── intro.md
├── LICENSE
├── project.clj
├── README.md
├── resources
├── src
│   └── external_jar_1
│       └── core.clj
└── test
    └── external_jar_1
        └── core_test.clj
 
6 directories, 6 files

Nejdůležitější je soubor project.clj, protože jeho obsahem se řídí nástroj Leiningen při provádění všech úkolů:

(defproject external-jar-1 "0.1.0-SNAPSHOT"
    :description "FIXME: write description"
    :url "http://example.com/FIXME"
    :license {:name "Eclipse Public License"
              :url "http://www.eclipse.org/legal/epl-v10.html"}
    :dependencies [[org.clojure/clojure "1.6.0"]]
    :main ^:skip-aot external-jar-1.core
    :target-path "target/%s"
    :profiles {:uberjar {:aot :all}})

Do projektu přidáme zdrojový kód třídy naprogramované v Javě. Pro jednoduchost se tento soubor nazvaný PrintHello umístí do podadresáře resources:

tree
 
.
├── doc
│   └── intro.md
├── LICENSE
├── project.clj
├── README.md
├── resources
│   └── PrintHello.java
├── src
│   └── external_jar_1
│       └── core.clj
└── test
    └── external_jar_1
        └── core_test.clj
 
6 directories, 7 files

Soubor PrintHello.java obsahuje pro jednoduchost třídu s jedinou statickou metodou:

public class PrintHello {
    public static void printHello() {
        System.out.println("Hello world!");
    }
}

Tato statická metoda se volá z hlavního programu (core.clj) následovně:

(ns external-jar-1.core
    (:gen-class))
 
(defn -main
    "Vstupni bod do aplikace."
    [& args]
    (PrintHello/printHello))

3. Vytvoření a otestování (uber)archivu s projektem

Aby bylo možné metodu ze třídy PrintHello použít, musíme třídu přeložit (ručně nebo z IDE):

pushd resources
javac PrintHello.java
popd

Následně můžeme program spustit, a to přímo nástrojem Leiningen:

lein run
 
Hello world!

Vše funguje v pořádku, a to z toho důvodu, že obsah adresáře resources je automaticky uložen na CLASSPATH, o čemž se můžeme velmi snadno přesvědčit:

lein classpath
 
/home/tester/temp/clojure-examples/p1/external-jar-1/test:
/home/tester/temp/clojure-examples/p1/external-jar-1/src:
/home/tester/temp/clojure-examples/p1/external-jar-1/dev-resources:
/home/tester/temp/clojure-examples/p1/external-jar-1/resources:
/home/tester/temp/clojure-examples/p1/external-jar-1/target/base+system+user+dev/classes:
/home/tester/.m2/repository/clojure-complete/clojure-complete/0.2.3/clojure-complete-0.2.3.jar:
/home/tester/.m2/repository/org/clojure/tools.nrepl/0.2.6/tools.nrepl-0.2.6.jar:
/home/tester/.m2/repository/org/clojure/clojure/1.6.0/clojure-1.6.0.jar

Při distribuci programu popř. před jeho nasazením na testovací a produkční servery se většinou provádí vytvoření „uberjaru“, který obsahuje jak vlastní program v Clojure přeložený do bajtkódu, tak i všechny závislé knihovny, včetně knihoven samotného Clojure. Vytvoření „uberjaru“ je jednoduché a zajistí ho následující tři příkazy (ve skutečnosti postačuje použít jen příkaz poslední, ovšem vyčištěním adresáře target se nic nezkazí):

lein clean
lein deps
lein uberjar

Výsledek je uložený v adresáři target/uberjar/, takže ho lze snadno otestovat:

java -jar target/uberjar/external-jar-1-0.1.0-SNAPSHOT-standalone.jar
 
Hello world!

Proč tento program funguje, tj. proč není problém s načtením třídy PrintHello osvětlí náhled do vytvořeného Java archivu:

unzip -l target/uberjar/external-jar-1-0.1.0-SNAPSHOT-standalone.jar |grep PrintHello
 
117  2016-09-12 19:09   PrintHello.java
407  2016-09-12 21:17   PrintHello.class

4. Druhý demonstrační příklad – přidání Java archivu do podadresáře resources

Ve druhém demonstračním příkladu si situaci poněkud zesložitíme, protože namísto pouhých .class souborů budeme chtít do projektu přidat celý Java archiv. Opět si pro začátek vytvoříme kostru nového projektu:

lein new app external-jar-2

Soubor src/external_jar2/core.clj vypadá stále stejně, samozřejmě až na rozdílné jméno projektu:

(ns external-jar-2.core
  (:gen-class))
 
(defn -main
    "Vstupni bod do aplikace."
    [& args]
    (PrintHello/printHello))
 

Do adresáře resources však vložíme pouze Java archiv a pro jistotu vymažeme původní zdrojový kód i vygenerovaný bajtkód:

pushd resources
javac PrintHello.java
jar cvf PrintHello.jar PrintHello.class 
added manifest
adding: PrintHello.class(in = 407) (out= 278)(deflated 31%)
rm PrintHello.java PrintHello.class
popd

Struktura projektu nyní vypadá následovně:

.
├── doc
│   └── intro.md
├── LICENSE
├── project.clj
├── README.md
├── resources
│   └── PrintHello.jar
├── src
│   └── external_jar_2
│       └── core.clj
└── test
    └── external_jar_2
        └── core_test.clj
 
6 directories, 7 files

5. Přímé spuštění projektu nástrojem Leiningen a otestování nastavení CLASSPATH

Java archiv s testovací třídou PrintHello je vytvořen a uložen do správného adresáře resources, takže by se mohlo zdát, že je vše v pořádku. Při pokusu o spuštění aplikace však dojde k chybě:

lein run
 
Exception in thread "main" java.lang.RuntimeException: No such namespace: PrintHello, compiling:(external_jar_2/core.clj:9:5)
        at clojure.lang.Compiler.analyze(Compiler.java:6464)
        at clojure.lang.Compiler.analyze(Compiler.java:6406)
...
...
...
... spousta více či méně neužitečných řádků :-)
...
...
...
        at clojure.main.main(main.java:37)
Caused by: java.lang.RuntimeException: No such namespace: PrintHello
        at clojure.lang.Util.runtimeException(Util.java:221)
        at clojure.lang.Compiler.resolveIn(Compiler.java:6910)
        at clojure.lang.Compiler.resolve(Compiler.java:6884)
        at clojure.lang.Compiler.analyzeSymbol(Compiler.java:6845)
        at clojure.lang.Compiler.analyze(Compiler.java:6427)
        ... 52 more

Proč k chybě došlo osvětlí nám již známý diagnostický příkaz:

lein classpath

/home/tester/temp/clojure-examples/p1/external-jar-2/test:
/home/tester/temp/clojure-examples/p1/external-jar-2/src:
/home/tester/temp/clojure-examples/p1/external-jar-2/dev-resources:
/home/tester/temp/clojure-examples/p1/external-jar-2/resources:
/home/tester/temp/clojure-examples/p1/external-jar-2/target/base+system+user+dev/classes:
/home/tester/.m2/repository/clojure-complete/clojure-complete/0.2.3/clojure-complete-0.2.3.jar:
/home/tester/.m2/repository/org/clojure/tools.nrepl/0.2.6/tools.nrepl-0.2.6.jar:
/home/tester/.m2/repository/org/clojure/clojure/1.6.0/clojure-1.6.0.jar

Vidíme, že nikde není nastavena přímá cesta k souboru PrintHello.jar! Jak ji však lze nastavit? Pokud bychom spouštěli JVM standardní cestou, stačilo by samozřejmě ruční nastavení CLASSPATH, ovšem při použití projektu Leiningen, který se stará o inicializaci JVM, je nutné použít odlišné řešení, a to konkrétně úpravu projektového souboru project.clj. Povšimněte si zvýrazněného řádku:

(defproject external-jar-2 "0.1.0-SNAPSHOT"
    :description "FIXME: write description"
    :url "http://example.com/FIXME"
    :license {:name "Eclipse Public License"
              :url "http://www.eclipse.org/legal/epl-v10.html"}
    :dependencies [[org.clojure/clojure "1.6.0"]]
    :main ^:skip-aot external-jar-2.core
    :target-path "target/%s"
    :resource-paths ["resources/PrintHello.jar"]
    :profiles {:uberjar {:aot :all}})

Za klíčem :resource-paths je vektor cest k Java archivům i dalším prostředkům. Zkusme test znovu:

lein run
 
Hello world!

Výborně, ještě si pro jistotu vypišme nastavení cest:

/home/tester/temp/clojure-examples/p1/external-jar-2/test:
/home/tester/temp/clojure-examples/p1/external-jar-2/src:
/home/tester/temp/clojure-examples/p1/external-jar-2/dev-resources:
/home/tester/temp/clojure-examples/p1/external-jar-2/resources/PrintHello.jar:
/home/tester/temp/clojure-examples/p1/external-jar-2/target/base+system+user+dev/classes:
/home/tester/.m2/repository/clojure-complete/clojure-complete/0.2.3/clojure-complete-0.2.3.jar:
/home/tester/.m2/repository/org/clojure/tools.nrepl/0.2.6/tools.nrepl-0.2.6.jar:
/home/tester/.m2/repository/org/clojure/clojure/1.6.0/clojure-1.6.0.jar

Vše je – alespoň pro tuto chvíli – v pořádku.

6. Vytvoření a otestování (uber)archivu s projektem

Vzhledem k tomu, že přímé spuštění aplikace nástrojem Leiningen bylo úspěšné, mohlo by se zdát, že stejně úspěšné bude i vytvoření „uberjaru“ a jeho případné nasazení na testovací server. Pojďme si tedy celý proces odzkoušet, a to již známými kroky popsanými v předchozích kapitolách:

lein clean
lein deps
lein uberjar

Očekávaný „uberjar“ se skutečně vytvořil, a to opět v podadresáři targets, ovšem při pokusu o spuštění aplikace již nebudeme tak úspěšní:

java -jar target/uberjar/external-jar-2-0.1.0-SNAPSHOT-standalone.jar
 
Exception in thread "main" java.lang.NoClassDefFoundError: PrintHello
        at external_jar_2.core$_main.doInvoke(core.clj:6)
        at clojure.lang.RestFn.invoke(RestFn.java:397)
        at clojure.lang.AFn.applyToHelper(AFn.java:152)
        at clojure.lang.RestFn.applyTo(RestFn.java:132)
        at external_jar_2.core.main(Unknown Source)
Caused by: java.lang.ClassNotFoundException: PrintHello
        at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
        ... 5 more

Příčinu tohoto neúspěchu musíme hledat v tom, že se obsah podadresáře resources při tvorbě Java archivu („uberjaru“) prostě bez dalších zásahů vloží do vytvářeného archivu, takže vlastně získáme „JAR v JARu“, což v tomto případě není korektní a zejména to není příjemné pro případné uživatele aplikace. Ostatně můžeme si to vyzkoušet sami:

unzip -l target/uberjar/external-jar-2-0.1.0-SNAPSHOT-standalone.jar | grep PrintHello
 
      743  2016-09-12 19:13   PrintHello.jar

7. Korektní řešení – použití lokálního Maven repositáře

Vzhledem k tomu, že nástroj Leiningen interně používá Maven repositáře, spočívá (pravděpodobně jediné) korektní řešení v tom, že z externího Java archivu, který budeme chtít do projektu přidat, vytvoříme v lokálním Maven repositáři, který většinou nalezneme v adresáři ~/.m2, nový artefakt. Tento adresář by měl být vytvořen minimálně ve chvíli instalace Leiningenu; pokud však adresář ~/.m2 nemůžete nalézt, vypište si nastavení proměnné prostředí M2_HOME, zda neukazuje na jiný adresář. Vraťme se však k našemu problému. Pokud máme nějaký Java archiv – typicky to bývají databázové drivery, k nimž nutně nemusí existovat zdrojové kódy – a potřebujeme ho distribuovat či nasazovat společně s aplikací, lze toho dosáhnout korektnějším způsobem, než pouhým uložením Java archivu do podadresáře resources. Doporučený způsob se skládá ze čtyř kroků:

  1. Použití pomocného projektu založeného na pluginu nazvaném lein-localrepo.
  2. Vygenerování artefaktu s Java archivem v lokálním Maven repositáři.
  3. Vložením příslušného řádku se jménem a verzí knihovny do projektového souboru (klíč :dependencies v souboru project.clj).
  4. Vytvořením „uberjaru“, který nyní bude mít korektní obsah.

Jednotlivé kroky si podrobněji popíšeme v navazujících kapitolách.

8. Pomocný projekt určený pro vložení externího Java archivu do lokálního Maven repositáře

Prvním krokem je vytvoření pomocného projektu, který nám bude sloužit pouze pro vygenerování lokálních artefaktů. Striktně řečeno vlastně ani není nezbytně nutné tento pomocný projekt vytvářet a používat, protože příslušný plugin lze specifikovat v libovolném jiném projektu, ale striktním oddělením tvorby artefaktu od zbytku „hlavního“ projektu můžeme celé problematice lépe porozumět. Vytvořme si tedy kostru zpočátku zcela prázdného projektu:

lein new app maven-artefact-generator

Následně provedeme úpravu projektového souboru přidáním klíče :plugins. Obsahem tohoto klíče je vektor, přičemž každý prvek vektoru specifikuje jeden plugin pro nástroj Leiningen. Na pluginy se můžeme dívat také jako na běžné knihovny, které ovšem do Leiningenu přidávají další cíle (při volání z CLI nové příkazy, ostatně již jsme si popisovali plugin codox či cloverage):

(defproject teiid-module "0.1.0-SNAPSHOT"
    :description "FIXME: write description"
    :url "http://example.com/FIXME"
    :license {:name "GNU General Public License"
              :url "https://gnu.org/licenses/gpl.html"}
    :dependencies [[org.clojure/clojure "1.7.0"]]
    :plugins [[lein-localrepo "0.5.3"]]
    :target-path "target/%s"
    :profiles {:uberjar {:aot :all}})

Nově požadovaný plugin si i s jeho závislostmi stáhneme:

lein deps

Nyní je tento pomocný projekt připraven a můžeme ho použít pro vytvoření artefaktů pro lokální Maven repositář.

9. Vygenerování artefaktu pro lokální Maven repositář

Podívejme se nyní na způsob vytvoření nového artefaktu. Nejprve do adresáře pomocného projektu zkopírujeme soubor PrintHello.jar, který jsme vytvořili ve čtvrté kapitole. Následně použijeme příkaz lein localrepo. Jak již zajisté tušíte, je tento nový příkaz zajišťován právě díky pluginu lein-localrepo a bude tedy fungovat pouze tehdy, když se nacházíme v daném projektu (o tom se ostatně sami můžete přesvědčit spuštěním příkazu lein bez parametrů uvnitř projektu a mimo pomocný projekt).

Příkaz lein localrepo nabízí několik variant spuštění. My konkrétně využijeme variantu install, které se předá název Java archivu, název artefaktu a jeho verze (tyto dva údaje si zapamatujte):

lein localrepo install resources/PrintHello.jar PrintHello 1.0.0

V adresáři s artefakty by se měl po zadání tohoto příkazu objevit nový podadresář PrintHello s následujícím obsahem:

tree ~/.m2/repository/PrintHello
 
/home/tester/.m2/repository/PrintHello
└── PrintHello
    ├── 1.0.0
    │   ├── _maven.repositories
    │   ├── PrintHello-1.0.0.jar
    │   └── PrintHello-1.0.0.pom
    └── maven-metadata-local.xml
 
2 directories, 4 files

10. Vytvořený artefakt

Zajímavé (i když nikoli nezbytné!) může být prozkoumání XML souborů s metadaty o nové knihovně:

cat ~/.m2/repository/PrintHello/PrintHello/maven-metadata-local.xml
 
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
  <groupId>PrintHello</groupId>
  <artifactId>PrintHello</artifactId>
  <versioning>
    <release>1.0.0</release>
    <versions>
      <version>1.0.0</version>
    </versions>
    <lastUpdated>20160912174143</lastUpdated>
  </versioning>
</metadata>

A dále pak prozkoumání souboru popisujícího artefakt:

cat ~/.m2/repository/PrintHello/PrintHello/1.0.0/PrintHello-1.0.0.pom
 
<?xml version="1.0" encoding="UTF-8"?>
<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">
  <modelVersion>4.0.0</modelVersion>
  <groupId>PrintHello</groupId>
  <artifactId>PrintHello</artifactId>
  <version>1.0.0</version>
  <name>PrintHello</name>
</project>

11. Třetí demonstrační příklad – přidání závislosti do našeho projektu

Nyní si ukažme, jakým způsobem se vlastně nový modul použije. Je to ve skutečnosti velmi jednoduché až triviální, protože všechny potřebné znalosti již známe z dřívějška. Postačuje do projektu vložit zvýrazněný řádek a současně odstranit předchozí řádek s klíčem :resource-paths. Projektový soubor může vypadat následovně:

(defproject external-jar-3 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [PrintHello "1.0.0"]]
  :main ^:skip-aot external-jar-3.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

Příkaz lein deps se již nebude pokoušet o hledání modulu PrintHello, protože ho v adresáři ~/.m2 nalezne (samozřejmě musíte použít shodné jméno i číslo verze!).

Projekt půjde přímo spustit:

lein run
 
Hello world!

Zajímavé je nastavení CLASSPATH (opět zvýrazním, řádek, který je v tuto chvíli důležitý):

/home/tester/temp/clojure-examples/p1/external-jar-3/test:
/home/tester/temp/clojure-examples/p1/external-jar-3/src:
/home/tester/temp/clojure-examples/p1/external-jar-3/dev-resources:
/home/tester/temp/clojure-examples/p1/external-jar-3/resources:
/home/tester/temp/clojure-examples/p1/external-jar-3/target/base+system+user+dev/classes:
/home/tester/.m2/repository/clojure-complete/clojure-complete/0.2.3/clojure-complete-0.2.3.jar:
/home/tester/.m2/repository/org/clojure/tools.nrepl/0.2.6/tools.nrepl-0.2.6.jar:
/home/tester/.m2/repository/PrintHello/PrintHello/1.0.0/PrintHello-1.0.0.jar:
/home/tester/.m2/repository/org/clojure/clojure/1.6.0/clojure-1.6.0.jar

12. Vytvoření a otestování (uber)archivu s projektem

V tento okamžik již Leiningen pracuje s modulem PrintHello stejně jako s jakýmkoli jiným modulem, takže můžeme bez problémů vytvořit funkční „uberjar“:

lein clean
lein deps
lein uberjar

Spuštění aplikace z vytvořeného Java archivu proběhne bez chyby:

widgety

java -jar target/uberjar/external-jar-2-0.1.0-SNAPSHOT-standalone.jar
 
Hello world!

Výpisem obsahu Java archivu zjistíme, že v tomto případě již nemáme „JAR v JARu“, ale správný obsah:

unzip -l target/uberjar/external-jar-3-0.1.0-SNAPSHOT-standalone.jar | grep PrintHello
 
      407  2016-09-12 19:12   PrintHello.class

Poznámka: na rozdíl od prvního příkladu se v Java archivu nenachází původní zdrojový soubor PrintHello.java, a to samozřejmě z toho důvodu, že tento soubor nebyl uložen do PrintHello.jar.

13. Odkazy na předchozí části tohoto seriálu

  1. Clojure 1: Úvod
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/
  2. Clojure 2: Symboly, kolekce atd.
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/
  3. Clojure 3: Funkcionální programování
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-3-cast-funkcionalni-programovani/
  4. Clojure 4: Kolekce, sekvence a lazy sekvence
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-4-cast-kolekce-sekvence-a-lazy-sekvence/
  5. Clojure 5: Sekvence, lazy sekvence a paralelní programy
    http://www.root.cz/clanky/clojure-a-bezpecne-aplikace-pro-jvm-sekvence-lazy-sekvence-a-paralelni-programy/
  6. Clojure 6: Podpora pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/
  7. Clojure 7: Další funkce pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/
  8. Clojure 8: Identity, stavy, neměnné hodnoty a reference
    http://www.root.cz/clanky/programovaci-jazyk-clojure-8-identity-stavy-nemenne-hodnoty-a-referencni-typy/
  9. Clojure 9: Validátory, pozorovatelé a kooperace s Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-9-validatory-pozorovatele-a-kooperace-mezi-clojure-a-javou/
  10. Clojure 10: Kooperace mezi Clojure a Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/
  11. Clojure 11: Generátorová notace seznamu/list comprehension
    http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/
  12. Clojure 12: Překlad programů z Clojure do bajtkódu JVM I:
    http://www.root.cz/clanky/programovaci-jazyk-clojure-12-preklad-programu-z-clojure-do-bajtkodu-jvm/
  13. Clojure 13: Překlad programů z Clojure do bajtkódu JVM II:
    http://www.root.cz/clanky/programovaci-jazyk-clojure-13-preklad-programu-z-clojure-do-bajtkodu-jvm-pokracovani/
  14. Clojure 14: Základy práce se systémem maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/
  15. Clojure 15: Tvorba uživatelských maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/
  16. Clojure 16: Složitější uživatelská makra
    http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/
  17. Clojure 17: Využití standardních maker v praxi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/
  18. Clojure 18: Základní techniky optimalizace aplikací
    http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/
  19. Clojure 19: Vývojová prostředí pro Clojure
    http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/
  20. Clojure 20: Vývojová prostředí pro Clojure (Vimu s REPL)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-20-vyvojova-prostredi-pro-clojure-integrace-vimu-s-repl/
  21. Clojure 21: ClojureScript aneb překlad Clojure do JS
    http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/
  22. Leiningen: nástroj pro správu projektů napsaných v Clojure
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure/
  23. Leiningen: nástroj pro správu projektů napsaných v Clojure (2)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-2/
  24. Leiningen: nástroj pro správu projektů napsaných v Clojure (3)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-3/
  25. Leiningen: nástroj pro správu projektů napsaných v Clojure (4)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-4/
  26. Leiningen: nástroj pro správu projektů napsaných v Clojure (5)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-5/
  27. Leiningen: nástroj pro správu projektů napsaných v Clojure (6)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-6/
  28. Programovací jazyk Clojure a databáze (1.část)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/
  29. Pluginy pro Leiningen
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-pluginy-pro-leiningen/
  30. Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi/
  31. Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi-2/
  32. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk
    http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk/
  33. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk-2/
  34. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure/
  35. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure (2)
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure-2/
  36. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure (3)
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure-3/
  37. Programovací jazyk Clojure a práce s Gitem
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem/
  38. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (dokončení)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk-dokonceni/
  39. Programovací jazyk Clojure a práce s Gitem (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem-2/
  40. Programovací jazyk Clojure – triky při práci s řetězci
    http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-retezci/
  41. Programovací jazyk Clojure – triky při práci s kolekcemi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-kolekcemi/
  42. Programovací jazyk Clojure – práce s mapami a množinami
    http://www.root.cz/clanky/programovaci-jazyk-clojure-prace-s-mapami-a-mnozinami/
  43. Programovací jazyk Clojure – základy zpracování XML
    http://www.root.cz/clanky/programovaci-jazyk-clojure-zaklady-zpracovani-xml/
  44. Programovací jazyk Clojure – testování s využitím knihovny Expectations
    http://www.root.cz/clanky/programovaci-jazyk-clojure-testovani-s-vyuzitim-knihovny-expectations/
  45. Programovací jazyk Clojure – některé užitečné triky použitelné (nejenom) v testech
    http://www.root.cz/clanky/programovaci-jazyk-clojure-nektere-uzitecne-triky-pouzitelne-nejenom-v-testech/
  46. Enlive – výkonný šablonovací systém pro jazyk Clojure
    http://www.root.cz/clanky/enlive-vykonny-sablonovaci-system-pro-jazyk-clojure/
  47. Nástroj Leiningen a programovací jazyk Clojure: tvorba vlastních knihoven pro veřejný repositář Clojars
    http://www.root.cz/clanky/nastroj-leiningen-a-programovaci-jazyk-clojure-tvorba-vlastnich-knihoven-pro-verejny-repositar-clojars/
  48. Novinky v Clojure verze 1.8.0
    http://www.root.cz/clanky/novinky-v-clojure-verze-1–8–0/
  49. Asynchronní programování v Clojure s využitím knihovny core.async
    http://www.root.cz/clanky/asynchronni-programovani-v-clojure-s-vyuzitim-knihovny-core-async/
  50. Asynchronní programování v Clojure s využitím knihovny core.async (pokračování)
    http://www.root.cz/clanky/asynchronni-programovani-v-clojure-s-vyuzitim-knihovny-core-async-pokracovani/
  51. Asynchronní programování v Clojure s využitím knihovny core.async (dokončení)
    http://www.root.cz/clanky/asynchronni-programovani-v-clojure-s-vyuzitim-knihovny-core-async-dokonceni/
  52. Vytváříme IRC bota v programovacím jazyce Clojure
    http://www.root.cz/clanky/vytvarime-irc-bota-v-programovacim-jazyce-clojure/
  53. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/

14. Odkazy na Internetu

  1. Clojure home page
    http://clojure.org/
  2. Clojure (downloads)
    http://clojure.org/downloads
  3. Clojure – Functional Programming for the JVM
    http://java.ociweb.com/mar­k/clojure/article.html
  4. Clojure quick reference
    http://faustus.webatu.com/clj-quick-ref.html
  5. 4Clojure
    http://www.4clojure.com/
  6. ClojureDoc (rozcestník s dokumentací jazyka Clojure)
    http://clojuredocs.org/
  7. Clojure (na Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  8. Clojure (na Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  9. Clojars:
    https://clojars.org/
  10. Seznam knihoven na Clojars:
    https://clojars.org/projects
  11. Zip archiv s Clojure 1.8.0
    http://repo1.maven.org/ma­ven2/org/clojure/clojure/1­.8.0/clojure-1.8.0.zip
  12. Clojure 1.8 is now available
    http://clojure.org/news/2016/01/19/clo­jure18
  13. Changes to Clojure in Version 1.8
    https://github.com/clojure/clo­jure/blob/master/changes.md
  14. Clojure core.async
    http://www.infoq.com/presen­tations/clojure-core-async
  15. core.async API Reference
    https://clojure.github.io/core.async/
  16. Clojure core.async Channels
    http://clojure.com/blog/2013/06/28/clo­jure-core-async-channels.html
  17. core.async examples
    https://github.com/clojure/co­re.async/blob/master/exam­ples/walkthrough.clj
  18. Timothy Baldridge – Core.Async
    https://www.youtube.com/wat­ch?v=enwIIGzhahw
Našli jste v článku chybu?
Vitalia.cz: Antibakteriální mýdla nepomáhají, spíš škodí

Antibakteriální mýdla nepomáhají, spíš škodí

Root.cz: Hořící telefon Samsung Note 7 zapálil auto

Hořící telefon Samsung Note 7 zapálil auto

Lupa.cz: Cimrman má hry na YouTube i vlastní doodle

Cimrman má hry na YouTube i vlastní doodle

120na80.cz: Hrbatá prsa aneb mýty o implantátech

Hrbatá prsa aneb mýty o implantátech

Měšec.cz: TEST: Vyzkoušeli jsme pražské taxikáře

TEST: Vyzkoušeli jsme pražské taxikáře

Vitalia.cz: Tahák, jak vyzrát nad zápachem z úst

Tahák, jak vyzrát nad zápachem z úst

DigiZone.cz: Technisat připravuje trojici DAB

Technisat připravuje trojici DAB

Podnikatel.cz: Udělali jsme velkou chybu, napsal Čupr

Udělali jsme velkou chybu, napsal Čupr

DigiZone.cz: Budoucnost TV vysílání ve Visegrádu

Budoucnost TV vysílání ve Visegrádu

Podnikatel.cz: Vytvořte si web sami. Redakční systém Tumblr

Vytvořte si web sami. Redakční systém Tumblr

Vitalia.cz: Tohle jsou nejlepší česká piva podle odborníků

Tohle jsou nejlepší česká piva podle odborníků

Podnikatel.cz: Nemá dluhy? Zjistíte to na poště

Nemá dluhy? Zjistíte to na poště

Vitalia.cz: Tesco nabízí desítky tun jídla zdarma

Tesco nabízí desítky tun jídla zdarma

120na80.cz: Galerie: Čínští policisté testují českou minerálku

Galerie: Čínští policisté testují českou minerálku

Vitalia.cz: Tradiční čínská medicína a rakovina

Tradiční čínská medicína a rakovina

Lupa.cz: Proč jsou firemní počítače pomalé?

Proč jsou firemní počítače pomalé?

Podnikatel.cz: Letáky? Lidi zuří, ale ony stále fungují

Letáky? Lidi zuří, ale ony stále fungují

DigiZone.cz: Sat novinky: NASA Ultra HD (4K)

Sat novinky: NASA Ultra HD (4K)

Vitalia.cz: dTest odhalil ten nejlepší kečup

dTest odhalil ten nejlepší kečup

DigiZone.cz: Parlamentní listy: kde končí PR...

Parlamentní listy: kde končí PR...