Obsah
1. Leiningen: nástroj pro správu projektů napsaných v Clojure (2)
2. Vytvoření nového demonstračního projektu
3. Výpis adresářů, v nichž se hledají přeložené třídy a zdrojové kódy
4. Přidání přeložené javovské třídy do projektu
5. Spuštění projektu s přidanou javovskou třídou
6. Interaktivní smyčka REPL integrovaná do Leiningenu
7. Vytvoření Java archivu s demonstračním projektem
8. Spuštění projektu s využitím Java archivu
9. Vytvoření „megaarchivu“ s projektem i se samotným Clojure
10. Spuštění aplikace z „megaarchivu“
1. Leiningen: nástroj pro správu projektů napsaných v Clojure (2)
V první části článku o nástroji Leiningen jsme si ukázali, jakým způsobem je možné tento nástroj jednoduše nainstalovat na prakticky jakýkoli stroj s JDK a JRE skriptem lein a jak lze s využitím tohoto nástroje vytvořit nový projekt, který bude napsán v programovacím jazyce Clojure. Taktéž jsme se seznámili se způsobem automatického stažení knihoven, na nichž projekt závisí i s tím, jak se projekt nakonec spustí. Ve skutečnosti je ovšem možné Leiningen používat i k dalším činnostem. Při vývoji lze s výhodou využít vestavěnou interaktivní smyčku REPL (Read-Eval-Print Loop), která se od běžné smyčky REPL implementované v samotném Clojure v mnoha ohledech odlišuje – existencí historie zadávaných příkazů, automatickým nahráním všech knihoven vyžadovaných pro běh projektu, vylepšeným systémem nápovědy apod.
Leiningen se ovšem v praxi velmi často používá i při vytváření javovských archivů (souborů s koncovkou .jar) obsahujících celý projekt, který tak lze jednoduše distribuovat či nasazovat na další počítače. Vytvořený archiv lze ale například využít i v tomto obrazu pro Docker. Dokonce lze vytvářet i takzvané „megaarchivy“ (uberjar) určené pro spuštění na počítači s JRE (Java Runtime Environment) – megaarchivy totiž obsahují i samotné jádro Clojure, samozřejmě ve verzi používané programátorem při vývoji aplikace. Leiningen podporuje i tvorbu a spouštění jednotkových testů, které sice nejsou ve výchozím nastavení kompatibilní s formátem používaným nástrojem JUnit (lze ovšem využít plugin test2junit), ovšem i tak lze s jejich pomocí relativně snadno vyvíjenou aplikaci, resp. její jednotlivé funkce, průběžně testovat. Způsobem tvorby jednotkových testů se budeme podrobněji zabývat příště.
2. Vytvoření nového demonstračního projektu
Před ukázkami dalších možností nabízených nástrojem Leiningen si ve vhodném adresáři vytvoříme zcela nový projekt, který budeme postupně měnit a v závěru článku také testovat. Již z předchozí části víme, jak se nový projekt vytvoří. Vše zařídí následující příkaz, který musí být spuštěn v adresáři, do něhož má právě aktivní uživatel právo zápisu:
lein new app clojure_test_2
Na standardní výstup by se měla vypsat následující zpráva:
Generating a project called clojure_test_2 based on the 'app' template.
Z hlášení vidíme, že se projekt skutečně podařilo vytvořit. Adresář s projektem by měl mít následující strukturu (stejnou, jako projekt vytvořený minule, samozřejmě až na odlišný název):
. ├── doc │ └── intro.md ├── LICENSE ├── project.clj ├── README.md ├── resources ├── src │ └── clojure_test_2 │ └── core.clj └── test └── clojure_test_2 └── core_test.clj 6 directories, 6 files
Do projektu ještě přidáme deklaraci závislosti na externí knihovně, pro jednoduchost stejné, jako tomu bylo i minule, tedy org.clojure.data/json. Změna je provedena v hlavním souboru celého projektu – project.clj:
(defproject clojure_test_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"] [org.clojure/data.json "0.2.5"]] :main ^:skip-aot clojure-test-2.core :target-path "target/%s" :profiles {:uberjar {:aot :all}})
Upravíme i zdrojový kód projektu takovým způsobem, aby tuto knihovnu využíval a volal z ní metodu pprint (povšimněte si, že jsme pro jmenný prostor knihovny vytvořili kratší a snadno zapamatovatelný alias json):
(ns clojure-test-2.core (:gen-class) (:require [clojure.data.json :as json])) (defn -main "I don't do a whole lot ... yet." [& args] (let [article {:title "What's Good About Clojure?" :url "http://www.catalysoft.com/articles/goodAboutClojure.html" :last-checked (.toString (new java.util.Date))}] (json/pprint article)))
3. Výpis adresářů, v nichž se hledají přeložené třídy a zdrojové kódy
Jedním z velmi častých problémů, s nimiž se setkávají jak programátoři vyvíjející aplikace v Javě, tak i administrátoři, je zjištění popř. nastavení classpath, tj. adresářů a Java archivů, v nichž virtuální stroj Javy po svém spuštění hledá přeložené třídy popř. další potřebné soubory (resources). Podobný problém řeší i vývojáři používající programovací jazyk Clojure, kde je situace ještě poněkud komplikovanější kvůli pravidlům pojmenovávání zdrojových souborů, adresářů a jmenných prostorů (zjednodušeně řečeno se pomlčky ve jmenných prostorech převádí na podtržítka). Pokud je však projekt založen nástrojem Leiningen, je možné zjistit aktuální nastavení classpath velmi jednoduše; konkrétně pomocí příkazu lein classpath. Můžeme si to snadno vyzkoušet:
lein classpath
Výstupem tohoto příkazu je jediný řádek, který jsem kvůli vyšší čitelnosti rozdělil na větší množství řádků v místě oddělovače jednotlivých cest:
/home/tester/src/Clojure/clojure_test_2/test: /home/tester/src/Clojure/clojure_test_2/src: /home/tester/src/Clojure/clojure_test_2/dev-resources: /home/tester/src/Clojure/clojure_test_2/resources: /home/tester/src/Clojure/clojure_test_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/data.json/0.2.5/data.json-0.2.5.jar: /home/tester/.m2/repository/org/clojure/clojure/1.6.0/clojure-1.6.0.jar
4. Přidání přeložené javovské třídy do projektu
Z výstupu příkazu lein classpath je patrné, že se přeložené třídy, tj. soubory s koncovkou .class budou vyhledávat mj. i v podadresáři resources, který byl automaticky vytvořen v průběhu generování adresářové struktury nového projektu. Ihned si tuto vlastnost otestujeme; přidáme totiž do projektu jeden zdrojový soubor napsaný v programovacím jazyce Java, jehož překladem vznikne kýžený soubor .class obsahující bajtkód přeložené třídy. Zdrojový soubor se bude jmenovat Adder.java a třída v něm deklarovaná bude mít (pochopitelně) název Adder, protože se jedná o veřejnou třídu:
public class Adder { public static int add(int x, int y) { return x+y; } }
Překlad této třídy do bajtkódu se (prozatím) provede ručním zavoláním standardního překladače javac:
javac Adder.java
Výsledný soubor se jménem Adder.class musí být přesunut do adresáře resources:
mv Adder.class resources/
Struktura projektu nyní vypadá následovně:
. ├── Adder.java ├── doc │ └── intro.md ├── LICENSE ├── project.clj ├── README.md ├── resources │ └── Adder.class ├── src │ └── clojure_test_2 │ └── core.clj └── test └── clojure_test_2 └── core_test.clj 6 directories, 8 files
5. Spuštění projektu s přidanou javovskou třídou
Nyní musíme otestovat, zda vyvíjená aplikace po svém spuštění skutečně třídu Adder „vidí“ a může volat její metody. Upravíme tedy zdrojový kód naší aplikace následujícím způsobem:
(ns clojure-test-2.core (:gen-class) (:require [clojure.data.json :as json])) (defn -main "I don't do a whole lot ... yet." [& args] (println (Adder/add 1 2)))
Zkusme si nyní takto upravenou aplikaci spustit a otestovat tak, zda se přeložená třída Adder skutečně po spuštění nalezne a zda bude korektně zavolána i metoda Adder.add():
lein run
Výstupem by měla být hodnota 3:
3
Projekt vrátíme do původního stavu vymazáním souborů Adder.java a Adder.class. Následně se upraví i zdrojový kód core.clj tak, jak je vypsán ve druhé kapitole.
6. Interaktivní smyčka REPL integrovaná do Leiningenu
Důležitou součástí nástroje Leiningen je i interaktivní smyčka REPL (Read-Eval-Print Loop), která je oproti standardnímu REPLu integrovanému přímo do Clojure vylepšena. Zejména je implementována historie příkazů, dále je možné se k REPLu připojit přes zvolený port (což dělají některá integrovaná vývojová prostředí), k dispozici je vylepšený systém nápovědy apod. Ovšem nejdůležitější je fakt, že se při inicializaci REPLu správně nastaví i cesty ke všem třídám a knihovnám, takže je možné bez dalších složitostí spouštět a testovat jednotlivé části projektu. Ostatně si to můžeme jednoduše vyzkoušet zadáním následujícího příkazu (příkaz se samozřejmě musí spustit z adresáře projektu):
lein repl
Po inicializaci by se měla na konzoli vypsat následující zpráva (verze JVM atd. samozřejmě může být odlišná):
nREPL server started on port 56416 on host 127.0.0.1 - nrepl://127.0.0.1:56416 REPL-y 0.3.5, nREPL 0.2.6 Clojure 1.6.0 OpenJDK 64-Bit Server VM 1.7.0_75-b13 Docs: (doc function-name-here) (find-doc "part-of-name-here") Source: (source function-name-here) Javadoc: (javadoc java-object-or-class-here) Exit: Control+D or (exit) or (quit) Results: Stored in vars *1, *2, *3, an exception in *e clojure-test-2.core=>
Nyní si můžeme vyzkoušet, jestli se opravdu načetla i knihovna pro práci s formátem JSON:
clojure-test-2.core=> json/pprint #<json$pprint clojure.data.json$pprint@69f528dc>
Symbol json/pprint je skutečně navázán na funkci, takže si vypišme nápovědu:
clojure-test-2.core=> (doc json/pprint) ------------------------- clojure.data.json/pprint ([x & options]) Pretty-prints JSON representation of x to *out*. Options are the same as for write except :value-fn, which is not supported. nil
Funkci je samozřejmě možné ihned zavolat:
clojure-test-2.core=> (json/pprint [:Clojure :is :awesome]) ["Clojure", "is", "awesome"] nil
7. Vytvoření Java archivu s demonstračním projektem
Další důležitou funkcí nabízenou nástrojem Leiningen je možnost vytvořit Java archiv (soubor s koncovkou .jar), který bude obsahovat jak vlastní projekt, tak i všechny knihovny, na kterých tento projekt závisí. Java archiv je posléze možné spustit na jiném počítači, na něm ovšem musí být nainstalovaný Clojure. Opět si tuto funkcionalitu vyzkoušíme. Nejdříve je nutné nepatrně upravit obsah souboru project.clj takovým způsobem, aby se provedl překlad zdrojového kódu clojure-test-2.core do bajtkódu, tedy do souborů .class. Úprava je jednoduchá: na řádku :main stačí vymazat metadata ^:skip-aot (já jsem změnu provedl poněkud odlišně – původní řádek je zakomentovaný a za ním je přidána nová deklarace):
(defproject clojure_test_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"] [org.clojure/data.json "0.2.5"]] ;:main ^:skip-aot clojure-test-2.core :main clojure-test-2.core :target-path "target/%s" :profiles {:uberjar {:aot :all}})
Vytvoření Java archivu zajistí příkaz lein jar, ještě předtím je však vhodné celý projekt vyčistit příkazem lein clean:
lein clean lein jar
Struktura projektu se rozšíří, protože vznikne adresář pojmenovaný target, v němž je mj. i náš projekt přeložený do souborů .class:
. ├── doc │ └── intro.md ├── LICENSE ├── project.clj ├── README.md ├── resources ├── src │ └── clojure_test_2 │ └── core.clj ├── target │ ├── classes │ │ ├── clojure │ │ │ └── data │ │ │ ├── json │ │ │ │ └── JSONWriter.class │ │ │ ├── json$codepoint_case.class │ │ │ ├── json$codepoint.class │ │ │ ├── json$codepoint_clause.class │ │ │ ├── json_compat_0_1__init.class │ │ │ ├── json$default_value_fn.class │ │ │ ├── json$default_write_key_fn.class │ │ │ ├── json$fn__23.class │ │ │ ├── json$fn__67.class │ │ │ ├── json$fn__70.class │ │ │ ├── json$fn__70$G__65__77.class │ │ │ ├── json$fn__70$G__66__73.class │ │ │ ├── json$fn__97.class │ │ │ ├── json$fn__97$fn__98.class │ │ │ ├── json__init.class │ │ │ ├── json$json_str.class │ │ │ ├── json$loading__4958__auto__.class │ │ │ ├── json$pprint_array.class │ │ │ ├── json$pprint_array$fn__105.class │ │ │ ├── json$pprint.class │ │ │ ├── json$pprint_dispatch.class │ │ │ ├── json$pprint_generic.class │ │ │ ├── json$pprint_generic$fn__133.class │ │ │ ├── json$pprint_json.class │ │ │ ├── json$pprint_object.class │ │ │ ├── json$pprint_object$fn__111.class │ │ │ ├── json$pprint_object$iter__114__118.class │ │ │ ├── json$pprint_object$iter__114__118$fn__119.class │ │ │ ├── json$pprint_object$iter__114__118$fn__119$fn__120.class │ │ │ ├── json$print_json.class │ │ │ ├── json$read_array.class │ │ │ ├── json$_read.class │ │ │ ├── json$read.class │ │ │ ├── json$read_decimal.class │ │ │ ├── json$read_escaped_char.class │ │ │ ├── json$read_hex_char.class │ │ │ ├── json$read_integer.class │ │ │ ├── json$read_integer$fn__44.class │ │ │ ├── json$read_json.class │ │ │ ├── json$read_number.class │ │ │ ├── json$read_number$fn__49.class │ │ │ ├── json$read_object.class │ │ │ ├── json$read_quoted_string.class │ │ │ ├── json$read_str.class │ │ │ ├── json$write_array.class │ │ │ ├── json$write_bignum.class │ │ │ ├── json$write.class │ │ │ ├── json$write_double.class │ │ │ ├── json$write_float.class │ │ │ ├── json$write_generic.class │ │ │ ├── json$write_json.class │ │ │ ├── json$write_named.class │ │ │ ├── json$write_null.class │ │ │ ├── json$write_object.class │ │ │ ├── json$write_plain.class │ │ │ ├── json$write_ratio.class │ │ │ ├── json$write_str.class │ │ │ └── json$write_string.class │ │ ├── clojure_test_2 │ │ │ ├── core.class │ │ │ ├── core$fn__148.class │ │ │ ├── core__init.class │ │ │ ├── core$loading__4958__auto__.class │ │ │ └── core$_main.class │ │ └── META-INF │ │ └── maven │ │ └── clojure_test_2 │ │ └── clojure_test_2 │ │ └── pom.properties │ ├── clojure_test_2-0.1.0-SNAPSHOT.jar │ └── stale │ └── extract-native.dependencies └── test └── clojure_test_2 └── core_test.clj
Podívejme se nyní podrobněji na obsah adresáře target:
ls -la target/
Na standardní výstup by se měla vypsat přibližně následující struktura:
total 108 drwxr-xr-x 4 tester tester 4096 Uno 14 22:25 . drwxr-xr-x 7 tester tester 4096 Uno 14 22:25 .. drwxr-xr-x 4 tester tester 4096 úno 15 21:35 base+system+user+dev drwxr-xr-x 5 tester tester 4096 Uno 14 22:25 classes -rw-r--r-- 1 tester tester 90184 Uno 14 22:25 clojure_test_2-0.1.0-SNAPSHOT.jar drwxr-xr-x 2 tester tester 4096 Uno 14 22:25 stale
Nejzajímavější je v tomto případě samozřejmě soubor clojure_test2-0.1.0-SNAPSHOT.jar. Jeho pojmenování vzniklo jednoduše – jedná se o název projektu, za nějž je připojeno číslo verze. Oba tyto údaje lze opět nalézt v souboru project.clj.
Můžeme samozřejmě prozkoumat obsah tohoto souboru, a to konkrétně s využitím nástroje jar:
jar tvf target/clojure_test_2-0.1.0-SNAPSHOT.jar
Nalezneme zde například celou strukturu se zdrojovými kódy projektu:
453 Sat Feb 14 22:25:56 CET 2015 project.clj 373 Sat Feb 14 22:18:06 CET 2015 clojure_test_2/core.clj
Dále různé metainformace obsahující mj. i jméno třídy, v níž se má při spuštění hledat statická metoda main:
0 Sat Feb 14 22:25:52 CET 2015 META-INF/ 126 Sat Feb 14 22:25:56 CET 2015 META-INF/MANIFEST.MF 453 Sat Feb 14 22:25:56 CET 2015 META-INF/leiningen/clojure_test_2/clojure_test_2/project.clj 479 Sat Feb 14 22:25:56 CET 2015 META-INF/leiningen/clojure_test_2/clojure_test_2/README.md 11218 Sat Feb 14 22:25:56 CET 2015 META-INF/leiningen/clojure_test_2/clojure_test_2/LICENSE
Dokonce se zde nachází metainformace použitelné známým nástrojem Maven (v souboru pom.properties jsou správně vypsány i závislosti atd. atd.):
0 Sat Feb 14 22:25:52 CET 2015 META-INF/maven/ 0 Sat Feb 14 22:25:52 CET 2015 META-INF/maven/clojure_test_2/ 0 Sat Feb 14 22:25:52 CET 2015 META-INF/maven/clojure_test_2/clojure_test_2/ 2003 Sat Feb 14 22:25:56 CET 2015 META-INF/maven/clojure_test_2/clojure_test_2/pom.xml 113 Sat Feb 14 22:25:52 CET 2015 META-INF/maven/clojure_test_2/clojure_test_2/pom.properties
Leiningen samozřejmě celý projekt přeložil ze zdrojových kódů přímo do javovského bajtkódu (překlad je komplikovanější, protože se provádí po funcích):
0 Sat Feb 14 22:25:56 CET 2015 clojure_test_2/ 1559 Sat Feb 14 22:25:56 CET 2015 clojure_test_2/core$_main.class 2996 Sat Feb 14 22:25:56 CET 2015 clojure_test_2/core__init.class 1859 Sat Feb 14 22:25:54 CET 2015 clojure_test_2/core$loading__4958__auto__.class 1338 Sat Feb 14 22:25:56 CET 2015 clojure_test_2/core$fn__148.class 1799 Sat Feb 14 22:25:54 CET 2015 clojure_test_2/core.class
A konečně můžeme v archivu nalézt i třídy knihovny clojure.data.json, která je naším testovacím projektem využívána:
0 Sat Feb 14 22:25:54 CET 2015 clojure/ 0 Sat Feb 14 22:25:56 CET 2015 clojure/data/ 2021 Sat Feb 14 22:25:56 CET 2015 clojure/data/json$pprint_dispatch.class 3219 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$write_object.class 2562 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$read_array.class 3035 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$loading__4958__auto__.class 722 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$fn__70$G__66__73.class 957 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$fn__97$fn__98.class 1168 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$pprint_object$fn__111.class 787 Sat Feb 14 22:25:56 CET 2015 clojure/data/json$pprint_object$iter__114__118.class 1294 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$write_float.class 1662 Sat Feb 14 22:25:56 CET 2015 clojure/data/json$pprint_generic.class 1378 Sat Feb 14 22:25:56 CET 2015 clojure/data/json$pprint_object.class 668 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$write_plain.class 1714 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$fn__70$G__65__77.class 3591 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$write.class 1545 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$fn__97.class 1365 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$write_ratio.class 1806 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$fn__23.class 1047 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$write_named.class 767 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$read_integer$fn__44.class 0 Sat Feb 14 22:25:54 CET 2015 clojure/data/json/ 138 Sat Feb 14 22:25:54 CET 2015 clojure/data/json/JSONWriter.class 1164 Sat Feb 14 22:25:56 CET 2015 clojure/data/json$pprint_json.class 2047 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$read_hex_char.class 1255 Sat Feb 14 22:25:56 CET 2015 clojure/data/json$print_json.class 1167 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$pprint_array$fn__105.class 701 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$write_null.class 588 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$default_value_fn.class 3661 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$codepoint_clause.class 3061 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$read.class 3574 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$read_object.class 970 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$codepoint.class 2866 Sat Feb 14 22:25:56 CET 2015 clojure/data/json$read_json.class 2588 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$read_number$fn__49.class 1039 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$write_bignum.class 1769 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$default_write_key_fn.class 1298 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$write_double.class 1213 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$read_str.class 2093 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$pprint_object$iter__114__118$fn__119$fn__120.class 1898 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$write_array.class 1884 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$fn__67.class 3002 Sat Feb 14 22:25:56 CET 2015 clojure/data/json$pprint.class 868 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$fn__70.class 1447 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$read_integer.class 1249 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$write_str.class 2646 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$read_escaped_char.class 1253 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$pprint_array.class 1166 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$read_decimal.class 2008 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$read_quoted_string.class 3083 Sat Feb 14 22:25:56 CET 2015 clojure/data/json$pprint_object$iter__114__118$fn__119.class 1290 Sat Feb 14 22:25:56 CET 2015 clojure/data/json$write_json.class 29064 Sat Feb 14 22:25:56 CET 2015 clojure/data/json__init.class 4993 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$_read.class 5390 Sat Feb 14 22:25:56 CET 2015 clojure/data/json_compat_0_1__init.class 2378 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$codepoint_case.class 1164 Sat Feb 14 22:25:56 CET 2015 clojure/data/json$json_str.class 1363 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$read_number.class 1659 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$write_generic.class 1667 Sat Feb 14 22:25:56 CET 2015 clojure/data/json$pprint_generic$fn__133.class 10739 Sat Feb 14 22:25:54 CET 2015 clojure/data/json$write_string.class
8. Spuštění projektu s využitím Java archivu
Java archiv vytvořený postupem popsaným v předchozí kapitole je možné použít pro spuštění aplikace. Musí se však zajistit, aby virtuální stroj jazyka Java našel i Java archiv se samotným runtime systémem Clojure. Pro jednoduchost předpokládejme, že soubor clojure-1.6.0.jar (popř. symbolický link na tento soubor) je umístěn v adresáři target. Spuštění je relativně snadné – musí se jen nastavit classpath a specifikovat jméno třídy s metodou main (což je v tomto případě shodné se jmenným prostorem našeho projektu):
cd target java -classpath clojure-1.6.0.jar:clojure_test_2-0.1.0-SNAPSHOT.jar clojure_test_2.core
Aplikace se skutečně korektně spustí:
{"title":"What's Good About Clojure?", "url":"http:\/\/www.catalysoft.com\/articles\/goodAboutClojure.html", "last-checked":"Sat Feb 14 22:21:57 CET 2015"}
Lze použít i následující zkratku (viz též man java):
java -cp "*" clojure_test_2.core
Archiv clojure-1.6.0.jar se samozřejmě může nacházet i v jiném adresáři:
java -cp /home/tester/src/Clojure/clojure-1.6.0.jar:clojure_test_2-0.1.0-SNAPSHOT.jar clojure_test_2.core
Kupodivu je možné použít i starší verze Clojure (to ovšem neplatí za všech okolností):
java -cp /home/tester/src/Clojure/clojure-1.5.1.jar:clojure_test_2-0.1.0-SNAPSHOT.jar clojure_test_2.core
9. Vytvoření „megaarchivu“ s projektem i se samotným Clojure
V předchozí kapitole ukázaný způsob spouštění aplikace z javovského archivu je sice použitelný, ovšem v mnoha případech může být výhodnější, aby archiv obsahoval i samotný Clojure. Tím se zajistí téměř stoprocentní přenositelnost takto zabalené aplikace i na další počítače, které musí obsahovat pouze běhové prostředí Javy (ve verzi minimálně stejné, jaká byla použita pro vytvoření archivu, i když toto omezení je možné obejít). Poměrně zajímavé může být použití tímto způsobem zabalené aplikace v obrazu systému pro Docker. Nástroj Leiningen tvorbu podobných „megaarchivů“ podporuje: nazývá je uberjar-y. Pro jejich vytvoření se používá příkaz lein uberjar, ovšem před jeho provedením je rozumné vyčistit adresář s projektem příkazem lein clean:
lein clean lein uberjar
Leiningen by po zadání těchto příkazů měl do adresáře s projektem vygenerovat další soubory, zejména adresář target/uberjar. Jednotlivé verze Leiningenu se ve svém chování nepatrně odlišují, nicméně získat byste měli přibližně následující strukturu. Soubor s megaarchivem, který nás nyní zajímá, je vyznačený tučným písmem:
. ├── doc │ └── intro.md ├── LICENSE ├── project.clj ├── README.md ├── resources ├── src │ └── clojure_test_2 │ └── core.clj ├── target │ ├── uberjar │ │ ├── clojure_test_2-0.1.0-SNAPSHOT-standalone.jar │ │ └── stale │ │ └── extract-native.dependencies │ └── uberjar+uberjar │ ├── classes │ │ ├── clojure │ │ │ └── data │ │ │ ├── json │ │ │ │ └── JSONWriter.class │ │ │ ├── json$codepoint_case.class │ │ │ ├── json$codepoint.class │ │ │ ├── json$codepoint_clause.class │ │ │ ├── json_compat_0_1__init.class │ │ │ ├── json$default_value_fn.class │ │ │ ├── json$default_write_key_fn.class │ │ │ ├── json$fn__23.class │ │ │ ├── json$fn__67.class │ │ │ ├── json$fn__70.class │ │ │ ├── json$fn__70$G__65__77.class │ │ │ ├── json$fn__70$G__66__73.class │ │ │ ├── json$fn__97.class │ │ │ ├── json$fn__97$fn__98.class │ │ │ ├── json__init.class │ │ │ ├── json$json_str.class │ │ │ ├── json$loading__4958__auto__.class │ │ │ ├── json$pprint_array.class │ │ │ ├── json$pprint_array$fn__105.class │ │ │ ├── json$pprint.class │ │ │ ├── json$pprint_dispatch.class │ │ │ ├── json$pprint_generic.class │ │ │ ├── json$pprint_generic$fn__133.class │ │ │ ├── json$pprint_json.class │ │ │ ├── json$pprint_object.class │ │ │ ├── json$pprint_object$fn__111.class │ │ │ ├── json$pprint_object$iter__114__118.class │ │ │ ├── json$pprint_object$iter__114__118$fn__119.class │ │ │ ├── json$pprint_object$iter__114__118$fn__119$fn__120.class │ │ │ ├── json$print_json.class │ │ │ ├── json$read_array.class │ │ │ ├── json$_read.class │ │ │ ├── json$read.class │ │ │ ├── json$read_decimal.class │ │ │ ├── json$read_escaped_char.class │ │ │ ├── json$read_hex_char.class │ │ │ ├── json$read_integer.class │ │ │ ├── json$read_integer$fn__44.class │ │ │ ├── json$read_json.class │ │ │ ├── json$read_number.class │ │ │ ├── json$read_number$fn__49.class │ │ │ ├── json$read_object.class │ │ │ ├── json$read_quoted_string.class │ │ │ ├── json$read_str.class │ │ │ ├── json$write_array.class │ │ │ ├── json$write_bignum.class │ │ │ ├── json$write.class │ │ │ ├── json$write_double.class │ │ │ ├── json$write_float.class │ │ │ ├── json$write_generic.class │ │ │ ├── json$write_json.class │ │ │ ├── json$write_named.class │ │ │ ├── json$write_null.class │ │ │ ├── json$write_object.class │ │ │ ├── json$write_plain.class │ │ │ ├── json$write_ratio.class │ │ │ ├── json$write_str.class │ │ │ └── json$write_string.class │ │ ├── clojure_test_2 │ │ │ ├── core.class │ │ │ ├── core$fn__148.class │ │ │ ├── core__init.class │ │ │ ├── core$loading__4958__auto__.class │ │ │ └── core$_main.class │ │ └── META-INF │ │ └── maven │ │ └── clojure_test_2 │ │ └── clojure_test_2 │ │ └── pom.properties │ ├── clojure_test_2-0.1.0-SNAPSHOT.jar │ └── stale │ └── extract-native.dependencies └── test └── clojure_test_2 └── core_test.clj
Podívejme se na velikost tohoto souboru, například příkazem ls:
ls -l target/uberjar
total 3728 -rw-r--r-- 1 tester tester 3811198 Uno 14 22:23 clojure_test_2-0.1.0-SNAPSHOT-standalone.jar drwxr-xr-x 2 tester tester 4096 Uno 14 22:23 stale
Vidíme, že se jedná o relativně velký archiv, ovšem není divu, protože obsahuje jak naši demonstrační aplikaci, tak i všechny potřebné knihovny (zde konkrétně clojure.data.json) i samotný Clojure. Pokud se chcete podívat na obsah tohoto archivu, lze opět použít nástroj jar:
jar tvf target/uberjar/clojure_test_2-0.1.0-SNAPSHOT-standalone.jar
10. Spuštění aplikace z „megaarchivu“
Ve chvíli, kdy je „megaarchiv“ vytvořen, je možné naši demonstrační aplikaci spustit velice snadno, a to následujícím způsobem:
cd target/uberjar java -jar clojure_test_2-0.1.0-SNAPSHOT-standalone.jar
Povšimněte si, že není zapotřebí zadávat jméno třídy, v níž se má vyhledat statická metoda main. Dříve, přesněji řečeno při snaze o spuštění aplikace z Java archivu bez přidaného Clojure, bylo nutné uvádět jméno této třídy, zde konkrétně clojure_test2.core, ve skutečnosti se ovšem při použití java -jar najde jméno třídy v metadatech přidaných do javovského archivu nástrojem Leiningen.
Po spuštění by se na standardní výstup měly vypsat tyto tři řádky:
{"title":"What's Good About Clojure?", "url":"http:\/\/www.catalysoft.com\/articles\/goodAboutClojure.html", "last-checked":"Sat Feb 14 22:24:41 CET 2015"}
11. Obsah třetí části článku
Ve třetí části článku o nástroji Leiningen se budeme podrobněji zabývat dvěma oblastmi. Tou první je práce s jednotkovými testy, tj. vytváření a následné spouštění testů. Druhou – podle mého názoru dosti zajímavou – oblastí je využití Leiningenu při tvorbě a ladění webových aplikací. Seznámíme se s knihovnami Ring (https://github.com/ring-clojure/ring) a Hiccup (https://github.com/weavejester/hiccup), které je možné použít pro velmi rychlou tvorbu kostry webových aplikací.
12. Odkazy na Internetu
- První část článku:
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure/ - Leiningen: úvodní stránka
http://leiningen.org/ - Leiningen: Git repository
https://github.com/technomancy/leiningen - leiningen-win-installer
http://leiningen-win-installer.djpowell.net/ - Clojure 1: Úvod
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/ - Clojure 2: Symboly, kolekce atd.
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/ - 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/ - 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/ - 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/ - Clojure 6: Podpora pro paralelní programování
http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/ - Clojure 7: Další funkce pro paralelní programování
http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/ - 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/ - 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/ - Clojure 10: Kooperace mezi Clojure a Javou
http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/ - Clojure 11: Generátorová notace seznamu/list comprehension
http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/ - 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/ - Clojure 13: Překlad programů z Clojure do bajtkódu JVM II
2) http://www.root.cz/clanky/programovaci-jazyk-clojure-13-preklad-programu-z-clojure-do-bajtkodu-jvm-pokracovani/ - Clojure 14: Základy práce se systémem maker
http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/ - Clojure 15: Tvorba uživatelských maker
http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/ - Clojure 16: Složitější uživatelská makra
http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/ - Clojure 17: Využití standardních maker v praxi
http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/ - Clojure 18: Základní techniky optimalizace aplikací
http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/ - Clojure 19: Vývojová prostředí pro Clojure
http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/ - 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/ - Clojure 21: ClojureScript aneb překlad Clojure do JS
http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/