Setup & Dependencies
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
Before any Bookshelf workbook or letter can be generated, the right POI jars must be on the classpath, and they must agree with each other. Most problems reported against POI are not API problems but classpath problems: a missing schema jar, two POI versions side by side, or a logging message that nobody can see. This page lists the artifacts, shows how to declare them with Maven and Gradle, and explains the transitive dependencies, the logging setup and the module-system details.
Versions are stated as of 2026-10-09: Apache POI 5.5.1 (released 30 November 2025; the 6.0 line has not been released), Apache XMLBeans 5.4.1 on Maven Central (POI 5.5.1 itself declares 5.3.0), Spring Boot 4.1.1 on Spring Framework 7 and Java 21. POI 5.5.x runs on Java 8 and later; the announced POI 6.0 needs JDK 17. The architecture behind the jars is explained in Introduction & Architecture, and the formats they read and write in File Formats: OOXML & OLE2.
Which artifact for which format
POI is split into several jars, all under the group id org.apache.poi. Pick them by file format, not by habit.
| Artifact | Contains | Needed for |
|---|---|---|
|
POIFS (OLE2 container), HPSF (document properties), HSSF ( |
Anything binary: |
|
OpenXML4J (the OPC package layer), XSSF ( |
Every |
|
The subset of the compiled OOXML schemas (XMLBeans classes in |
Pulled in automatically by |
|
Every OOXML schema class; about 14 MB in 5.5.1 (the POI FAQ says "around 16mb"). |
Digital signatures and any low-level |
|
HWPF ( |
Reading old |
The rule of thumb is: a Bookshelf export with books-inventory.xlsx or loan-letter.docx needs only poi-ooxml. Add
poi-scratchpad only when users still upload .doc files, and swap lite for full only when a NoClassDefFoundError tells
you to (see below).
Maven
Declare a single property for the POI version and use it for every POI artifact. There is no poi-bom, so the property is the
way to keep versions aligned.
<properties>
<java.version>21</java.version>
<poi.version>5.5.1</poi.version>
</properties>
<dependencies>
<!-- .xlsx (XSSF, SXSSF) and .docx (XWPF); brings poi and poi-ooxml-lite -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<!-- Optional: legacy .doc, .ppt and .msg -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>${poi.version}</version>
</dependency>
</dependencies>
Source: Apache POI, components overview.
Declaring poi-ooxml is enough for .xls too, because it depends on poi. If the project reads .xls only, depend on poi
alone and skip the much larger OOXML jars.
Gradle (Kotlin DSL)
The same dependencies in build.gradle.kts, with the version kept in one place.
val poiVersion = "5.5.1"
dependencies {
// .xlsx (XSSF, SXSSF) and .docx (XWPF)
implementation("org.apache.poi:poi-ooxml:$poiVersion")
// Optional: legacy .doc, .ppt and .msg
implementation("org.apache.poi:poi-scratchpad:$poiVersion")
}
Source: Apache POI, components overview.
Never mix POI versions
All org.apache.poi artifacts of a project must have exactly the same version. The jars share internal classes and package
layouts, and a poi jar of one release with a poi-ooxml of another typically fails at run time with NoSuchMethodError,
NoClassDefFoundError or a changed behaviour that is hard to trace. The usual way in is a transitive dependency: a
third-party library (a report engine, a Tika parser module, an old internal starter) that declares its own POI version, and
the build tool picks a different one for part of the artifacts.
Check the resolved versions, and force the whole group when a library drags in another release.
mvn dependency:tree -Dincludes=org.apache.poi
./gradlew dependencies --configuration runtimeClasspath | grep 'org.apache.poi'
configurations.all {
resolutionStrategy.eachDependency {
if (requested.group == "org.apache.poi") {
useVersion(poiVersion)
}
}
}
In Maven, declare the POI artifacts you use directly (as above) so that "nearest definition wins" selects your version, or
list them in dependencyManagement with ${poi.version}.
Source: Apache POI, FAQ (versions must match) and the Gradle dependency resolution documentation.
Transitive dependencies
poi-ooxml 5.5.1 brings in a small set of libraries. Their versions below come from the published POM files and change from
release to release, so read them with dependency:tree rather than relying on this table.
| Library | Version in POI 5.5.1 | Role |
|---|---|---|
|
5.3.0 |
Binds the OOXML schemas to Java classes (the |
|
2.24.3 (BOM) |
Only the logging API; there is no logging implementation (see the next section). |
|
1.28.0 |
Zip handling and streaming. |
|
2.21.0 |
Stream helpers. |
|
4.5.0 |
Collections used internally. |
|
1.20.0, 3.6.1, 1.3 |
Through |
|
1.08 |
Geometry support for Visio and drawing shapes. |
Some other dependencies (Bouncy Castle, Apache Santuario xmlsec, Batik, PDFBox) are declared as optional in the POM: they
are used for signing, encryption variants and drawing features and are not on your classpath unless you add them. The need for
Bouncy Castle with signatures is covered on the encryption page of this section.
XMLBeans 5.4.1 is available on Maven Central, but the POI project documents that XMLBeans 5.2.2 together with
log4j-api 2.24.1 has an issue, so do not override the XMLBeans version without testing. Source:
https://poi.apache.org/ (home page) and the Apache XMLBeans site.
Lite versus full OOXML schemas
By default poi-ooxml depends on poi-ooxml-lite, which contains only the schema classes that the POI tests touch. That
keeps a service small (about 6 MB instead of about 14 MB). The trade-off is that a low-level call can reach a class that is
not in the lite jar. The symptom is a stack trace such as:
java.lang.NoClassDefFoundError: org/openxmlformats/schemas/drawingml/x2006/chart/CTSomething
The fix recommended by the POI FAQ is to use poi-ooxml-full instead of lite. In Maven, exclude lite from poi-ooxml and
add full:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-lite</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-full</artifactId>
<version>${poi.version}</version>
</dependency>
In Gradle, exclude lite on the dependency and add full:
dependencies {
implementation("org.apache.poi:poi-ooxml:$poiVersion") {
exclude(group = "org.apache.poi", module = "poi-ooxml-lite")
}
implementation("org.apache.poi:poi-ooxml-full:$poiVersion")
}
Source: Apache POI, components overview, which lists poi-ooxml-full as the
alternative to lite.
Do not put lite and full on the classpath together: they contain the same classes, and both declare the same module name (see JPMS below). Prefer the lite jar and switch only when the error appears, because the full jar adds startup and packaging cost. Chart creation, digital signatures and some drawing features are the typical reasons for the switch; the exact pages where that matters are marked in the later pages of this section.
The logging bridge
Since POI 5.1.0 the only logging dependency is log4j-api. It is an API, not an implementation: when no Log4j 2 core is
present, Log4j falls back to a minimal console logger and prints a status message saying that no logging implementation was
found. In a Spring Boot service that means POI warnings (for example a missing font or a skipped feature) either go to the
console in a different format or are lost, and they never reach the Logback configuration that everything else uses.
Spring Boot already solves this. spring-boot-starter-logging (a dependency of every starter) includes
log4j-to-slf4j, which implements the Log4j API on top of SLF4J, so POI messages are written by Logback. With Spring Boot
4.1.1 nothing has to be added to the POM.
In an application that is not Spring Boot and uses Logback or another SLF4J binding, add the bridge yourself.
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
<version>${log4j.version}</version>
</dependency>
dependencies {
implementation("org.apache.logging.log4j:log4j-to-slf4j:$log4jVersion")
}
Use the same Log4j 2 version as the log4j-api that is resolved (Spring Boot 4.1.1 manages 2.25.5). Never add
log4j-slf4j-impl or log4j-slf4j2-impl at the same time as log4j-to-slf4j: they route in opposite directions and make an
endless loop. To control the verbosity of POI, configure the logger for the org.apache.poi package in the Logback
configuration, see Logging in Spring Boot.
Source: Apache POI, logging framework and Log4j 2 API.
Spring Boot dependency management
Spring Boot’s spring-boot-dependencies BOM (version 4.1.1) manages log4j-api through its log4j2.version property, but
it does not manage any org.apache.poi artifact and does not manage Apache XMLBeans. That has two consequences.
-
You must write the POI version yourself, as in the examples above, for both the Maven parent and the Gradle
io.spring.dependency-managementplug-in. -
Upgrading Spring Boot may silently change the
log4j-apithat POI sees. It is a compatible API, so this is harmless, but it is the reasondependency:treemay show alog4j-apinewer than the one in the POI POM.
If a Spring Boot property is available to pin versions, it is the property you define yourself (poi.version above).
The Spring Boot starter basics are in Spring Boot getting started.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.1.1</version>
</parent>
Source: Spring Boot, dependency versions.
JPMS module names
POI jars are not Java modules with a module-info.class; they are automatic modules, and their names are fixed by an
Automatic-Module-Name entry in the manifest. The names in 5.5.1 (read from the jar manifests) are below. All five jars are
multi-release jars.
| Artifact | Automatic module name |
|---|---|
|
|
|
|
|
|
|
|
|
|
A modular application declares them in its own module-info.java:
module com.bookshelf.reports {
requires org.apache.poi.ooxml;
requires org.apache.poi.poi;
}
Two points are worth remembering. First, lite and full have the same module name, so the module system will refuse to resolve both on the module path; this is one more reason to use only one. Second, automatic modules read every other module and the class path, so running a Spring Boot fat jar on the class path (the default) avoids all of this. The module system itself is covered in Packages and Modules.
Source: Java SE 21, ModuleFinder
(automatic modules) and the META-INF/MANIFEST.MF of each jar.
OSGi notes
The POI 5.5.1 manifests contain Automatic-Module-Name but no Bundle-SymbolicName, so the plain jars are not OSGi
bundles. In an OSGi container (Karaf, Equinox, Felix) there are two usual routes: wrap the jars into bundles with a tool such
as bnd, or use a distribution that provides ready-made bundles. Keep these points in mind in both cases.
-
POI and XMLBeans use reflection and the context class loader to find schema classes and factories, so every package must be visible to the bundle that loads them.
-
The multi-release jars should be wrapped with the tool’s multi-release support, or the JDK 9+ classes are lost.
-
The same single-version rule applies: one
poi, onepoi-ooxmland one schema jar per container.
This is advice about packaging, not a tested recipe; verify with your own container before relying on it.
Source: Apache POI, components overview.
Dependency table for the Bookshelf scenario
This table maps each Bookshelf file to the dependencies it needs, so the right set can be chosen up front.
| Bookshelf task | Dependencies | Notes |
|---|---|---|
|
|
Chart creation may require |
|
|
SXSSF is in |
|
|
XWPF; some |
|
|
Reading with |
|
|
Content controls may use low-level beans. |
Upload of old |
|
HSSF. |
Upload of old |
|
HWPF. |
Password-protected files, signatures |
|
See the encryption page of this section. |
Log output |
|
Already present with Spring Boot. |
Source: Apache POI, components overview.
Checklist
-
Use
poi-ooxmlfor.xlsxand.docx,poifor.xlsonly andpoi-scratchpadfor.doc. -
Keep all POI artifacts at one version through a property and check with
dependency:tree. -
Start with lite; switch to full on
NoClassDefFoundError: org/openxmlformats/schemas/..., never both. -
Make sure Log4j API output reaches your logging: Spring Boot does it, others add
log4j-to-slf4j. -
Spring Boot does not manage the POI version; you do.