{"id":"libraries-and-tools-build-tools","slug":"build-tools","title":"Build tools: Maven and a project's layout","text":"Every program in this course so far was a few files that Java Arena compiled and ran for you. Real projects are bigger, and they almost never start from nothing: they use **libraries**, code that other people wrote, tested and packaged for anyone to use. You've used two already. JUnit (module 26) is a library for unit tests, and JavaFX (modules 48 to 53) is a library for windows. On this site they were simply there: Java Arena puts JUnit and its practice version of JavaFX on the class path for you.\n\nOn your own computer, someone has to download the right version of each library, put it where javac and java can find it, compile the files in the right order, run the tests and pack the result. A **build tool** does all of that from a description of the project. The MOOC's exercise templates use **Maven**, one of the two build tools most Java projects use (the other is Gradle).\n\nThis module is about the tools that sit around a program. Maven, libraries and databases need your own computer: nothing on this page runs Maven or opens a database. The programs you write here use only the JDK, and the \"database\" in them is a data access object that keeps its data in memory, in a list or a map. Each step says what you'd do on your own computer.\n\n### A Maven project's folders\n\nMaven expects every project to have the same folders, so anyone who knows Maven finds their way around a new project at once:\n\n```text\nrepair-queue/\n    pom.xml\n    src/\n        main/\n            java/\n                com/example/repairs/\n                    Main.java\n                    RepairJob.java\n        test/\n            java/\n                com/example/repairs/\n                    RepairJobTest.java\n    target/\n```\n\n- `pom.xml` describes the project. It's at the top, in the project's own folder.\n- `src/main/java` holds the program's own classes, in the folders of their packages (module 41): the class `com.example.repairs.Main` is in `src/main/java/com/example/repairs/Main.java`.\n- `src/test/java` holds the test classes, in the same packages, so a test can use the classes it tests without imports, package-private parts included. They're compiled and run for the tests, and they aren't part of the finished program.\n- `target` is where Maven puts what it makes: compiled classes, test reports and the finished jar. Maven creates it, and you can delete it any time: the next build makes it again.\n\n### pom.xml\n\nPOM stands for Project Object Model. The file is written in XML, a text format of nested **elements**: `<version>1.0-SNAPSHOT</version>` is an element named `version` whose content is `1.0-SNAPSHOT`, and an element can hold other elements. A small `pom.xml` looks like this:\n\n```xml pom.xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>repair-queue</artifactId>\n    <version>1.0-SNAPSHOT</version>\n\n    <properties>\n        <maven.compiler.release>21</maven.compiler.release>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>4.13.2</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>\n```\n\nThe first lines are the same in every pom.xml: they say which kind of XML file this is. The rest is about this project:\n\n- **groupId, artifactId and version** name the project. The groupId is usually a domain name turned around, like a package name (`com.example`); the artifactId is the project's own name (`repair-queue`); the version is its version. `SNAPSHOT` at the end means \"still being worked on\": a finished release drops it (`1.0`).\n- **properties** are settings. `maven.compiler.release` is the Java version to compile for, and the source encoding says the files are UTF-8.\n- **dependencies** lists the libraries the project uses.\n\n### A dependency\n\nEvery library is named the same three ways as the project itself. Those three names are a library's **coordinates**, often written on one line with colons between them: `junit:junit:4.13.2`, or `com.h2database:h2:2.2.224` for the database library of step 4. To use a library, you add a `dependency` element with its coordinates inside `dependencies`:\n\n```xml\n<dependency>\n    <groupId>com.h2database</groupId>\n    <artifactId>h2</artifactId>\n    <version>2.2.224</version>\n</dependency>\n```\n\nA dependency can also have a **scope**. JUnit's above is `<scope>test</scope>`: the library is there when the tests are compiled and run, and not in the program itself, since the program never uses JUnit. A dependency without a scope is there everywhere.\n\n### Where libraries come from\n\nMaven downloads libraries from **Maven Central**, a large public repository of Java libraries. You can search it at [central.sonatype.com](https://central.sonatype.com) or through [mvnrepository.com](https://mvnrepository.com), which the MOOC mentions; each library's page shows the `dependency` element to copy, with its newest version.\n\nMaven keeps what it downloads on your computer, in a **local repository**: a folder named `.m2/repository` in your home folder. A jar's place there comes from its coordinates. The dots of the groupId become folders, then come a folder for the artifactId and one for the version, and the jar is named after the artifactId and the version. So `com.h2database:h2:2.2.224` is kept as:\n\n```text\n.m2/repository/com/h2database/h2/2.2.224/h2-2.2.224.jar\n```\n\nThe next build finds it there and downloads nothing. A library can need libraries of its own: JUnit 4.13.2 needs Hamcrest Core 1.3, so Maven downloads that too, without a line about it in your pom.xml. These are called **transitive dependencies**.\n\n### On your own computer\n\nTo build a Maven project on your own computer, install a JDK (version 21, as on this site) and Maven 3.9 or newer, or an editor that comes with Maven, such as IntelliJ IDEA, NetBeans (which the MOOC uses) or VS Code with its Java extensions. These editors open a folder with a pom.xml as a project and run Maven for you from their menus. In a terminal, in the project's folder:\n\n```shell\nmvn compile\nmvn test\nmvn package\nmvn compile exec:java -Dexec.mainClass=com.example.repairs.Main\n```\n\n- `mvn compile` downloads what's missing and compiles `src/main/java` into `target/classes`.\n- `mvn test` compiles the tests too and runs them, with a report of what passed.\n- `mvn package` does both and packs the program into a jar in `target` (step 5).\n- The last line runs the program's `main` with the exec plugin, which Maven downloads the first time.\n\nThe first build takes a while, since Maven downloads its own plugins too; later ones are fast. The MOOC's [section 14.4](https://java-programming.mooc.fi/part-14/4-maven-and-third-party-libraries) shows how its exercise templates use Maven.\n\n### And here\n\nJava Arena runs no Maven. It compiles your files with javac, and JUnit and its practice JavaFX are already on the class path. The folders of packages still work the same way here as on your own computer, and the files of a challenge are what would go in `src/main/java`.\n","task":"A small tool for a project prints where a class's file goes in a Maven project. Fill in the blanks so that `sourcePath` returns the path of a class's file from its full name: `src/main/java`, then the package's folders, then the class's name and `.java`. The program prints:\n\n```\nsrc/main/java/com/example/repairs/Main.java\nsrc/main/java/com/example/repairs/RepairJob.java\n```\n","kind":"fill","seed":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(sourcePath(\"com.example.repairs.Main\"));\n        System.out.println(sourcePath(\"com.example.repairs.RepairJob\"));\n    }\n\n    public static String sourcePath(String className) {\n        return \"[[src/main/java]]/\" + className.[[replace]](\".\", \"/\") + \".java\";\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(sourcePath(\"com.example.repairs.Main\"));\n        System.out.println(sourcePath(\"com.example.repairs.RepairJob\"));\n    }\n\n    public static String sourcePath(String className) {\n        return \"src/main/java/\" + className.replace(\".\", \"/\") + \".java\";\n    }\n}\n","hints":["The program's own classes go in `src/main/java` (the tests in `src/test/java`). After it comes one folder for each part of the package name.","The dots of `com.example.repairs.Main` become the slashes of `com/example/repairs/Main`: that's `className.replace(\".\", \"/\")`."],"tests":[{"name":"Two classes of one package","stdin":"","expect":"src/main/java/com/example/repairs/Main.java\nsrc/main/java/com/example/repairs/RepairJob.java","hidden":false},{"name":"Test 2","stdin":"","call":"System.out.println(sourcePath(\"org.kiln.schedule.Firing\"));\nSystem.out.println(sourcePath(\"Main\"));\n","expect":"src/main/java/org/kiln/schedule/Firing.java\nsrc/main/java/Main.java","hidden":true}],"require":[],"forbid":[],"more":[{"task":"Library pages often give a dependency as one line of coordinates, `group:artifact:version`, and sometimes a fourth part, the scope: `junit:junit:4.13.2:test`. Write the method `printDependency(String coordinates)`, which prints the `dependency` element for pom.xml, with its parts on lines of their own, indented by 4 spaces:\n\n- `<dependency>`\n- the groupId, the artifactId and the version, each in its element\n- only when the coordinates have a fourth part: the scope in a `scope` element\n- `</dependency>`\n\nThe program calls it for `com.h2database:h2:2.2.224` and prints:\n\n```\n<dependency>\n    <groupId>com.h2database</groupId>\n    <artifactId>h2</artifactId>\n    <version>2.2.224</version>\n</dependency>\n```\n","kind":"code","seed":"public class Main {\n    public static void main(String[] args) {\n        printDependency(\"com.h2database:h2:2.2.224\");\n    }\n\n    public static void printDependency(String coordinates) {\n\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        printDependency(\"com.h2database:h2:2.2.224\");\n    }\n\n    public static void printDependency(String coordinates) {\n        String[] parts = coordinates.split(\":\");\n        System.out.println(\"<dependency>\");\n        System.out.println(\"    <groupId>\" + parts[0] + \"</groupId>\");\n        System.out.println(\"    <artifactId>\" + parts[1] + \"</artifactId>\");\n        System.out.println(\"    <version>\" + parts[2] + \"</version>\");\n        if (parts.length == 4) {\n            System.out.println(\"    <scope>\" + parts[3] + \"</scope>\");\n        }\n        System.out.println(\"</dependency>\");\n    }\n}\n","hints":["`coordinates.split(\":\")` gives the parts in an array: the groupId at index 0, the artifactId at 1 and the version at 2.","The array's `length` is 4 when there's a scope. Print its line only then, between the version and `</dependency>`."],"tests":[{"name":"H2, without a scope","stdin":"","expect":"<dependency>\n    <groupId>com.h2database</groupId>\n    <artifactId>h2</artifactId>\n    <version>2.2.224</version>\n</dependency>","hidden":false},{"name":"printDependency(\"junit:junit:4.13.2:test\");","stdin":"","call":"printDependency(\"junit:junit:4.13.2:test\");\n","expect":"<dependency>\n    <groupId>junit</groupId>\n    <artifactId>junit</artifactId>\n    <version>4.13.2</version>\n    <scope>test</scope>\n</dependency>","hidden":true},{"name":"Test 3","stdin":"","call":"printDependency(\"org.hamcrest:hamcrest-core:1.3\");\nprintDependency(\"org.openjfx:javafx-controls:21.0.12:compile\");\n","expect":"<dependency>\n    <groupId>org.hamcrest</groupId>\n    <artifactId>hamcrest-core</artifactId>\n    <version>1.3</version>\n</dependency>\n<dependency>\n    <groupId>org.openjfx</groupId>\n    <artifactId>javafx-controls</artifactId>\n    <version>21.0.12</version>\n    <scope>compile</scope>\n</dependency>","hidden":true}],"require":[],"forbid":[]},{"task":"In Maven's local repository, a library's jar is in folders named after its coordinates, as the lesson showed. What does this program print?\n","kind":"predict","seed":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(jarPath(\"junit:junit:4.13.2\"));\n        System.out.println(jarPath(\"org.hamcrest:hamcrest-core:1.3\"));\n    }\n\n    public static String jarPath(String coordinates) {\n        String[] parts = coordinates.split(\":\");\n        String group = parts[0].replace(\".\", \"/\");\n        String artifact = parts[1];\n        String version = parts[2];\n        return group + \"/\" + artifact + \"/\" + version + \"/\" + artifact + \"-\" + version + \".jar\";\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(jarPath(\"junit:junit:4.13.2\"));\n        System.out.println(jarPath(\"org.hamcrest:hamcrest-core:1.3\"));\n    }\n\n    public static String jarPath(String coordinates) {\n        String[] parts = coordinates.split(\":\");\n        String group = parts[0].replace(\".\", \"/\");\n        String artifact = parts[1];\n        String version = parts[2];\n        return group + \"/\" + artifact + \"/\" + version + \"/\" + artifact + \"-\" + version + \".jar\";\n    }\n}\n","hints":["Only the groupId's dots become slashes. The version keeps its dots, and so does the artifactId (it has none here).","`junit` has no dots, so its folder is just `junit`, then the artifactId's folder `junit`, then the version's folder, then the jar's name."],"tests":[{"name":"Output","stdin":"","expect":"junit/junit/4.13.2/junit-4.13.2.jar\norg/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar","hidden":false}],"require":[],"forbid":[],"lines":["junit/junit/4.13.2/junit-4.13.2.jar","org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar"]}]}