Skip to content
Snippets Groups Projects
Commit fb8ab9be authored by Juuso Rytilahti's avatar Juuso Rytilahti
Browse files

Removed finnish images

parent 0aa612c4
Branches
No related tags found
No related merge requests found
# Build automation ja dependency management: Esimerkkinä Maven
Tässä harjoituksessa tutustutaan koontiautomaatioon tai käännösautomaatioon (build automation) ja riippuvuuksien/kirjastojen hallintaan (dependency management). Työkaluja on useita erilaisia, vaikka niistä kaikista löytyykin samat pääpiirteet.
Javan osalta käsitellään **maven**, mutta tämän ohjeen lopussa on listattuna vaihtoehtoja. Ei siis kannata jäädä liikaa jumiin maveniin, sillä työelämässä saattaa tulla vastaan muitakin työkaluja. Maven on kuitenkin melko suosittu. Käytännössä käytettävä työkalu valikoituu ohjelmoijan omien mieltymysten mukaan ja mistäpä muustakaan ohjelmoijat tykkäävät väitellä kuin työkalusta.
## Dependency management
Ohjelmointikielen mukana tulee *standardikirjasto* joka sisältää kaikki peruskäytössä tarvittavat metodit ja funktiot, kuten `print()` yms. sekä lukuisia oleellisia tyyppejä, kuten luvut, listat, sanakirjat ym. tietorakenteet. Vaikka oletuskirjastot ovatkin erittäin laajoja kokonaisuuksia, ne eivät silti sisällä kaikkea, eikä näin ole tarkoituskaan. Tämän takia eri ohjelmointikieliä varten on satoja ja tuhansia *kirjastoja* joiden avulla toiminnallisuutta saadaan laajennettua. Kirjasto on käytännössä siis vain jonkun muun tekemää koodia, joka ratkaisee jonkin tietyn ongelman. Esimerkkejä kirjastoista ovat esimerkiksi:
* Json-parsintakirjastot
* Tietokannanhallinta
* Grafiikkakirjastot
* Pelimoottorit ja kehykset
### Dependency management
Riippuvuuksienhallinta on oleellinen ongelma, joka täytyy ratkaista jos projektista tulee vähänkään laajempi. Riippuen ohjelmointikielestä, joskus kirjastoja tarvitaan ehdottomasti ainakin muutama ja isommassa projektissa niitä voi olla kymmeniä tai satakin erilaista. Ei siis ole mitenkään järkevää ajatella, että nämä pitäisi ladata käsin ja niitä pitäisi siirrellä, kuin tavallisia tiedostoja (vaikka sekin onkin mahdollista). Lisäksi kirjastoista on eri versioita. Sivuhuomioina, ongelmia on erityisesti javascript -maailmassa, jossa metodien nimiä ja parametreja saatetaan muuttaa versioiden välillä. Tällöin ei siis ole mahdollista vain ladata uusinta versioita. Oikeastaan kirjastojen hallinnassa on tärkeää asettaa seuraavat vaatimukset:
Tietystä kirjastosta halutaan..
* Täsmälleen tietty versio
* Vähintään tietty versio (käytetty ominaisuus lisätty vasta tietyssä versiossa)
* Enintään tietty versio (käytetty ominaisuus muuttuu erilaiseksi uusissa versioissa)
# Maven
Tässä ohjeessa esitellään mavenin perusominaisuudet. Mavenia käsitellään kahdella tavalla. Ensinnäkin se käsitellään itsenäisenä ohjelmana, jota voi suorittaa komentorivin avulla. Lisäksi, koska aiemmalla viikolla käsiteltiin kehitysympäristöt, esitellään myös mavenin liittäminen kehitysympäristöön, jolloin se on integroitu kätevästi play-nappulan painamiseen.
Jatka lukemista kohdasta [ide-integraatio.md](https://gitlab.utu.fi/TKO_2116/viikko-3/-/blob/main/ide-integraatio.md)
### IDE -integraatio
IDE:n tarkoitus on nimenomaan integroida kehitystyökalut yhteen. Tutustutaan seuraavaksi mavenin IntelliJ-integraatioon. Huomaa, että kaiken pystyy kyllä tekemään komentoriviltäkin ja kaikenlaisissa automaattisissa koontijärjestelmissä tämäkin on välttämätön taito. Peruskäyttäjä pääsee pitkälle kuitenkin ihan sillä *play* -nappulallakin.
# Uusi Maven-projekti
Valitse uusi projekti ja täytä asetukset järkevästi:
`GroupId` edustaa tämän ohjelman ryhmää. Se näyttä vähän kuin URLilta, mutta väärässä järjestykssä. Voit käyttää esim jotakin seuraavista:
* `oman.kotisivun.osoite`
* `fi.utu.gitlab.omatunnus`
* `com.github.projektinnimi`
ArtifactId on tästä projektista kootun ohjelmapaketin nimi.
<img src="kuvat/mvn2.png" alt="">
# Maven-työkalurivin avaaminen
Tämä onnistuu helposti valitsemalla View->Tool Windows -> Maven. Tämä avaa uuden näkymän joka näyttää tältä:
<img src="kuvat/mvn1.png" alt="">
Vasemmanpuoleisin kuvake on `reload` jolla saa päivitettyä koko projektin, esimerkiksi kun lataa uusia kirjastoja. Siitä oikealle `download` -nappulalla voi myös ladata erinäisiä asioita. Oleellisin on vihreän play-nappulan näköinen toiminto, jolla mavenin eri **goal**eja eli maaleja, jotka ovat mavenin avulla suoritetun käännös- ja koontityön vaiheiden osia.
Siirry seuraavaksi tiedostoon [pom-xml.md](https://gitlab.utu.fi/TKO_2116/viikko-3/-/blob/main/pom-xml.md).
\ No newline at end of file
# Mavenin perusteita
Jos luit jo READMEn, ide-integraation ja pom-xml -ohjeen, voit siirtyä testailemaan.
### Esimerkkiprojektin ajaminen
Voit suorittaa edellisessä vaiheessa luodun esimerkkiprojektisi edelleen normaalisti, play-nappulan avulla, eikä tämä tee varsinaisesti mitään sen kummallisempaa.
Voit myös suorittaa jonkin maven -komennon. Huomaa, että maven ei sellaisenaan suorita java-ohjelmaa, vaan siihen tarvitaan lisäosa (voit tutustua aiheeseen pluginit -kansiosta, mutta tämä ei liene valmis vielä luennon aikana).
Mavenin tarkoituksena on erityisesti **koota** projekti, eli muodostaa siitä valmis, kokonainen ohjelma. Maven suorittaa vaiheita (phase) ja voit antaa komennon joka määrittää suoritettavan vaiheen:
* validate: varmistaa, että kaikki tiedot ovat saatavilla ja oikein
* compile: käännä lähdekoodi
* test-compile: käännä testauskoodi (huom. ensi viikon aihe!)
* test: suorita yksikkötestit (huom. ensi viikon aihe!)
* package: paketoi ohjelma, esim. `.jar` -paketiksi
* integration-test: siirrä paketti testausympäristöön ja suorita integraatiotestit
* install: asenna paketti paikalliseen pakettivarastoon
* deploy: kopioi pakett etärepositorioon (ts. julkaise)
Kun suoritat jonkin vaiheen, maven suoritttaa myös *kaikki edelliset vaiheet*. Tässä ohjeessa tutustumme lähinnä `compile` ja `package` -vaiheisiin.
### Compile -vaihe
Compile -vaihe suorittaa käännöksen.
Projektin alkutilanne ennen kuin mitään on tehty:
<img src="kuvat/projektialku.png" alt="">
Jos siis haluat vain kääntää ohjelmasi kaikki java-luokat, voit tehdä sen suorittamalla `mvn compile`. Sen jälkeen projektipuu näyttää tältä:
<img src="kuvat/compile.png" alt="">
Hakemisto `target` sisältää siis kaikki mavenin kääntämät luokat ja joitakin asetus- ja lokitiedostoja. Tämä vaikuttaa heti monitmukaisemmalta rakenteelta kuin pelkän `javac` käyttäminen ja niin sen onkin, mutta tämä luo erittäin hyvän pohjan sille, että oikeassa projektissa saattaa olla tuhansia luokkia ja muita tiedostoja, joiden hallinta pelkästään käsipelillä olisi mahdotonta.
Maven tulostaa jotakin tällaista:
```
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------< fi.utu.tko2116:MavenEsim >----------------------
[INFO] Building MavenEsim 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ MavenEsim ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ MavenEsim ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /home/vilho/MavenEsim/target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.722 s
[INFO] Finished at: 2023-02-03T11:19:41+02:00
[INFO] ------------------------------------------------------------------------
Process finished with exit code 0
```
Niin kauan kuin kaikki on vain `INFO`a niin homma on ok. `WARNING` kannattaa huomioida ja `ERROR` ilmaisee että jotain meni pieleen. Esimerkiksi, jos joltain riviltä puuttuu puolipiste:
```
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------< fi.utu.tko2116:MavenEsim >----------------------
[INFO] Building MavenEsim 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ MavenEsim ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ MavenEsim ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /home/vilho/MavenEsim/target/classes
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /home/vilho/MavenEsim/src/main/java/fi/utu/tko2116/Main.java:[5,43] ';' expected
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.608 s
[INFO] Finished at: 2023-02-03T11:25:27+02:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project MavenEsim: Compilation failure
[ERROR] /home/vilho/MavenEsim/src/main/java/fi/utu/tko2116/Main.java:[5,43] ';' expected
[ERROR]
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-[ERROR] /home/vilho/MavenEsim/src/main/java/fi/utu/tko2116/Main.java:[5,43] ';' expectedrun Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
Process finished with exit code 1
```
Java-virheen löytämienn tästä voi olla melko hankalaa, mutta se löytyy jopa kahdesta kohdasta:
`[ERROR] /home/vilho/MavenEsim/src/main/java/fi/utu/tko2116/Main.java:[5,43] ';' expected`
Eli virhe on rivillä 5 ja 43. merkki kyseisellä rivillä: puolipiste puuttuu.
Edelleen java-ohjelmia voi ja kannattaa kehittää ihan sillä "play-nappulalla" mutta pitää muistaa että mvn on komentorivityökalu joka toimii ilman intelliJtäkin. mvn ei tietenkään myöskään käännä mitään itse, vaan käyttää java-kääntäjää.
### Package -vaihe
Varsinkin jos luokkatiedostoja on useita, esimerkiksi jos Main-luokan lisäksi on vaikka tutoriaalissakin mahdollisesti nähdyt `Henkilö.java`, `Kello.java` ja `Levysoitin.java`, compile-vaihe on kyllä edelleen tärkeä, mutta käännetyn ohjelman hallinnointi on hakalampaa, kun luokkia on useita. Tätä varten java-ohjelmat pakataan yleensä `.jar` -paketiksi (java archive) tai `.war` -paketiksi (web applicat archive, tai web application resource) jos kyseessä on web-sovellus.
Tietenkin jälleen `jar` paketointi hoituu komentorivityökalulla, mutta sivuutamme siihen tutustumisen. Sen sijaan hoidamme homman automaattiseti suorittamalla `mvn package` jolloin saamme valmiin paketin.
<img src="kuvat/jar.png" alt="">
Tämän paketin voit suorittaa nyt komentoriviltä, komennolla
`java -jar MavenEsim-1.0-SNAPSHOT.jar` ... tai voisit, jos olisit määritellyt, että projektista löytyy main-metodi.
Mavenia käytetään usein erilaisten kirjastojen tekemiseen. Tällöin main-metodia ei ole, vaan ohjelma sisältää pelkkiä luokkia, jotka ovat tarkoitettu muiden käyttöön. Koska nyt laadimme esimerkkisovellusta, meidän tulee määritellä `plugin` eli eräänlainen laajennos, joka vastaa siitä, että main-metodia koskeva määrittely kirjataan pakettiin.
Tämä määrittely löytyy `pluginit/build.xml` -tiedostosta, tästä repositoriosta (ks [pluginit-hakemisto](https://gitlab.utu.fi/TKO_2116/viikko-3/-/tree/main/pluginit)).
```xml
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
<build>
<plugins>
<!-- Plugini tulee tähän! Varo ettet kopioi build ja plugins-tagia kahdesti-->
</plugins>
</build>
</project>
```
Jos nyt suoritat uudelleen `mvn package` ja sen jälkeen `java -jar MavenEsim-1.0-SNAPSHOT.jar` ohjelma toimii.
Tutustutaan seuraavaksi viimeiseen osioon, eli riippuvuuksien lataamiseen: [riippuvuudet.md](https://gitlab.utu.fi/TKO_2116/viikko-3/-/blob/main/riippuvuudet.md).
## POM
Siinä missä `git` on ohjelma ja kaikki repositorion tiedon säilytetään projektin hakemistoon luodussa hakemistossa `.git`, mavenin käyttö kiteytyy pääasiassa komentorivityökaluun `mvn` joka käsittelee projetkin hakemistossa majailevaa `pom.xml` -tiedostoa.
Mavenin asetukset laaditaan XML-muodossa. POM eli *Project Object Model* määrittelee projektin tiedot, sekä riippuvuuksien ja koontiasetusten yksityiskohdat. Kaikki tämä määritellään tiedostossa `pom.xml`. Tämä tiedosto tallennetaan projektin juurihakemistoon, josta maven löytää sen käytettäessä.
Esimerkki "tyhjästä" konfiguraatiosta:
```xml
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
</project>
```
Lyhyt selostus XML-formaatista niille, jotka eivät sitä tunne: Kyseessä on tagipohjainen tapa järjestellä tietoja. Kuten esimerkissä näkyy, tagi alkaa aina `<näin>` ja loppuu `</näin>`. Niiden välissä voi olla muitakin tietoja. Tietojenkäsittelytieteellisesti tietorakenne on [puun (tree)](https://en.wikipedia.org/wiki/Tree_(data_structure)) muotoinen. Huomaa, että tagien sisällä voi olla myös muita tageja. Esimerkiksi koko määrittely on yhden `<project>` -tagin sisällä ja kaikki muu on sen sisällä.
* `<modelVersion>` on tämän konfiguraatiomallin versio ja se kuuluu olla näin.
* `<groupId>` määrittää paketin ryhmän. Tämä voi olla esimerkiksi ihan vain paketin tarkoitus tai vaikka firman/organisaation nimi. Tähän tulee perinteisesti `ylätasonverkkotunnus.firma.ohjelma`. Esimerkiksi Googlen julkaisema json-parseri *gson* on `com.google.gson`
* `<artifactId>` on tämän varsinaisen ohjelman/kirjaston nimi.
* `<version>` on tämän ohjelman/kirjaston versio. Jos teet muutoksia tai parannuksia, muuta versionumeroa yhdellä isommaksi ja julkaise päivitys sitten. Tyypillisesti versionumerot ovat esimerkiksi muotoa `major.minor.bugfix`. Suuret, mahdollisesti yhteensopivuuden rikkovat päivitykset tai muuten valtavat muutokset määritellään uudella `major`-versiolla. Pienemmät uudet ominaisuudet ja parannukset `minor`-versiolla ja usein julkaistavat bugikorjaukset merkataan kasvattamalla viimeistä numeroa. Tähän ei oikeastaan ole mitään standardeja, mutta em. tulkinta on varsin yleisessä käytössä.
Jos XML on täysin vieras formaatti, virheitä aiheuttanee eniten se, että mikä on minkäkin sisällä.
Seuraavaksi käsitellään [perusteita ohjeesta perusteet.md](https://gitlab.utu.fi/TKO_2116/viikko-3/-/blob/main/perusteet.md)
\ No newline at end of file
### Riippuvuuksien määrittely
Riippuvuuksilla tarkoitetaan pääasiassa kirjastojen hallintaa (ks. README.md). Tässä määritellään esimerkin avulla, miten voitaisiin ladata joitakin kirjastoja.
Eräs ominaisuus, joka javasta puuttuu, on JSON eli JavaScript Object Notation -muotoisen tekstin parsiminen takaisin oliomuotoon. Tämän pitäisi siis ladata riippuvuutena. Eräs json-toteutus on Googlen tekemä `gson` joka löytyy täältä: <https://mvnrepository.com/artifact/com.google.code.gson/gson>.
* Valitse versi, esimerkiksi uusin (tehtävät testattu versiolla 2.10). Maven antaa valmiiksi konfiguraation lukuisia eri ohjelmia varten. Valitaan kuitenkin maven:
Aluksi kannattaa lisätä `pom.xml` tiedostoon uusi tagi, dependencies:
```xml
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
<!-- Riippuvuudet merkataan tähän-->
<dependencies></dependencies>
</project>
```
Kopioi sitten mavenin antama koodipätkä ja lisää se `dependencies` tagin sisälle:
```xml
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
<!-- Riippuvuudet merkataan tähän-->
<dependencies>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10</version>
</dependency>
<!-- jos sinulla olisi muita riippuvuuksia, ne tulisivat tähän>
<dependency>
<groupId>joku.muu</groupId>
<artifactId>joku</artifactId>
<version>0.0.0</version>
</dependency>
-->
</dependencies>
</project>
```
Päivitä projektisi, niin IntelliJ käynnistää mavenin joka lataa sitten kaikki tarvittavat kirjastot.
### Testikoodia
Lisää projektiisi luokka `Henkilo.java`
```java
class Henkilo{
String nimi;
int ika;
public Henkilo(String nimi, int ika) {
this.nimi = nimi;
this.ika = ika;
}
@Override
public String toString() {
return "Henkilo{" +
"nimi='" + nimi + '\'' +
", ika=" + ika +
'}';
}
}
```
Lisää main-metodiisi seuraava koodi:
Importit:
```java
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
```
main-metodi:
```java
public static void main(String[] args) {
Henkilo h = new Henkilo("erkki",18);
//luodaan Json (Google Json) parseri
Gson g = new GsonBuilder().setPrettyPrinting().create();
//muutetaan olio merkkijonoksi
String s = g.toJson(h);
System.out.println(s);
// Vastaava Json-muotoinen teksti
String hlo = "{\"nimi\":\"erkki\",\"ika\":18}";
// muutetaan teksti Henkilo olioksi.
Henkilo x = g.fromJson(hlo, Henkilo.class);
System.out.println(x);
}
```
Sivuhuomioina, jos asia on vieras: luokissa käytetty `toString()` siis määrittää, miltä olio näyttää kun se muunnetaan merkkijonoksi ja tulostetaan. JSON puolestaan on yleinen standardi, joka määrittää, miltä oliot listoineen, taulukoineen ja kaikkine muine attribuutteineen esitetään JSON-muodossa. JSON on käytännössä sellaista, että voit liittää sen javascript-koodiin ja monesti myös python-koodin ja saat kelvollisen olion tai dictionaryn ja voit ajaa sitä sellaisenaan.
Yleisesti ottaen java-esimerkissämme on kyse **serialisoinnista**. Eli miten tietokoneen muistissa ympäriinsä oleva abstraki olio serialisoidaan, eli muunnetaan yhdeksi pötköksi, kuten merkkijonoksi, jolloin sen tallentaminen esimerkiksi tiedostoon tai tietokantaan, tai sen lähettäminen verkon yli on helppoa. Serialisoitu muoto mahdollistaa myös olion muuntamisen kätevästi takaisin alkuperäiseen muotoon.
# Seuraavaksi?
Tässä oli hyvin lyhyt esittely Mavenista ja sen perusteista.
[Maven Central](https://mvnrepository.com/repos/central) on järkevä paikka erinäisten pakettin lataamiseen. Jos katsot esimerkiksi [Gson-paketin](https://mvnrepository.com/artifact/com.google.code.gson/gson) paketin sivua, voi valita listalta tarvitsemasi version (yleensä uusin). Version valittuasi löydät kopioitavan pätkän konfiguraatiotiedostoasi varten:
<img src="kuvat/lataa.png" alt="">
Huomaa, että eri välilehdissä on tarjolla konfiguraatio myös muita koontityökaluja varten. Nämä ovat toki vain javaa varten. Pythonilla ja esimerkiksi javascriptillä on molemmilla täysin omat systeemit.
[Jetbrainsin omassa ohjeessa](https://www.jetbrains.com/help/idea/2022.3/maven-projects-tool-window.html?utm_source=product&utm_medium=link&utm_campaign=IC&utm_content=2022.3#context) on selostettu tarkemmin, miten maven tool window toimii ja millaisia ominaisuuksia siihen liittyy.
[Mavenin ohjeesta](https://maven.apache.org/) löytyy myös tarkka dokumentaatio kaikista ominaisuuksista, mutta kuten kaiken dokumentaation suhteen yleensä päteen, ne eivät ole varsinaisia käyttöoppaita.
kuvat/compile.png

44.6 KiB

kuvat/jar.png

38.9 KiB

kuvat/mvn1.png

11.7 KiB

kuvat/mvn2.png

50 KiB

kuvat/projektialku.png

28.4 KiB

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment