-
Notifications
You must be signed in to change notification settings - Fork 3
Module Layout
A source module is a directory containing module-info.java, with Java source and resources arranged beneath it by package:
-- module-info.java
`-- com/example/application/
|-- Application.java
`-- messages.properties
There is no required separation between production and test sources and resources. Colocating tests and benchmarks with the implementation is recommended when they need access to package-private code. Tools define class and package name suffix conventions that identify content to filter out when packaging a module.
Dedicated test and benchmark modules with ordinary requirements are a better fit when they use only public APIs. A multi-release layout is also supported for release-specific source and resources.
For a layout more suitable for source control or to support multiple modules, place each module using the module source path convention in src:
src/
|-- com.example.api/
| `-- module-info.java
|-- com.example.application/
| `-- module-info.java
`-- com.example.application.test/
`-- module-info.java
From the repository root, from src itself, or from anywhere inside an existing module source path, ja init creates the new module beneath src:
ja init com.example.applicationA directory named src is treated as a module source path when it is empty or contains the standard <module>/module-info.java structure. An unrelated layout such as src/main/java is left alone. The directory structure and module declarations are enough to discover the modules; there is no additional build file to maintain.
The working directory continues to select scope: run from the repository root or src to include every module, and from a module directory or one of its descendants to select that module. Use -C to make the same choice without first changing directories:
ja compile
ja -C src/com.example.application compileFor white-box tests, you can keep tests and their resources alongside the implementation:
com.example.application/
|-- module-info.java
`-- com/example/application/
|-- Application.java
|-- ApplicationTest.java
`-- test/
|-- Fixture.java
`-- fixture.properties
Add JUnit as a static requirement so it is available when compiling and testing, but is not required when the application runs:
ja require --static org.junit.jupiterWhen the module is packaged, the class and package suffixes declared by the tool determine which supporting content is left out.
For black-box tests against exported APIs, use a dedicated module with ordinary requirements. Making JUnit a regular requirement ensures that tests and resources remain in the packaged module:
module com.example.application.test {
requires com.example.application;
requires org.junit.jupiter; // @6.1.3
}Benchmarks follow the same pattern. Add JMH as a static requirement when benchmarks live with the implementation:
ja require --static org.openjdk.jmh.coreThe requirement activates the benchmark runner. Use an ordinary requirement on org.openjdk.jmh.core in a dedicated benchmark module.
Use the standard JMOD sections for modules that contain more than classes and resources:
com.example.tool/
|-- bin/
|-- classes/
| |-- module-info.java
| `-- com/example/tool/Tool.java
|-- conf/
|-- include/
|-- legal/
|-- lib/
`-- man/
Java source and resources go beneath classes. Only the sections used by the module need to be present. Exporting a module with this layout produces a jmod in addition to its jar.
Keep resources alongside the Java source that uses them. Their module-relative paths are preserved when the module is packaged.
Use META-INF/versions/<release> for release-specific source and resources:
com.example.application/
|-- META-INF/MANIFEST.MF
|-- META-INF/versions/21/com/example/application/
| |-- Application.java
| `-- messages.properties
|-- module-info.java
`-- com/example/application/
|-- Application.java
`-- messages.properties
Set Multi-Release: true in the manifest. For the selected release, files beneath META-INF/versions/<release> override matching files in the base module.