| name | dependency-compatibility |
| description | Invoke this skill to resolve third-party library compatibility issues during Neo to CF migration. Detects libraries that break on Java 25, Jakarta EE 10, or HANA Cloud — including schema migration tools (Liquibase, Flyway), DI frameworks (Guice, CDI), and other known problematic dependencies. Run after jakarta-java25-migration and sdk-replacement. |
| disable-model-invocation | false |
| allowed-tools | Read, Edit, Write, Bash, Grep, Glob |
Third-Party Dependency Compatibility
Resolve third-party library compatibility issues that arise during the Neo to Cloud Foundry migration.
Purpose
When migrating from Neo (Java 8, Java EE) to CF (Java 25, Jakarta EE 10, HANA Cloud), many third-party libraries break due to:
- Java 25 module system — removed
javax.xml.bind, javax.xml.ws, and other javax.* modules from the JDK
- javax → jakarta namespace — libraries compiled against
javax.servlet, javax.ws.rs, etc. won't link against Jakarta EE 10
- Database changes — schema migration tools may not recognize HANA Cloud
- DI framework conflicts — listener ordering, JNDI assumptions, and initialization sequences break when the platform changes
This skill scans the project's dependencies, identifies known compatibility issues, and applies targeted fixes.
Note: The jakarta-java25-migration skill handles the core javax→jakarta migration and Java 25 language upgrade. This skill handles third-party library-specific issues that the core migration doesn't cover.
Detection
This skill applies if any of these patterns are found:
In pom.xml
<artifactId>liquibase-core</artifactId>
<artifactId>flyway-core</artifactId>
<artifactId>guice</artifactId>
<artifactId>guice-servlet</artifactId>
<artifactId>weld-servlet-core</artifactId>
<artifactId>poi-ooxml</artifactId>
<artifactId>itextpdf</artifactId>
<artifactId>ehcache</artifactId>
<artifactId>hazelcast</artifactId>
<artifactId>quartz</artifactId>
<artifactId>retrofit</artifactId>
In Java source files
import com.google.inject.*;
import com.google.inject.servlet.GuiceServletContextListener;
import liquibase.Liquibase;
import liquibase.integration.servlet.LiquibaseServletListener;
import javax.enterprise.context.*;
import jakarta.enterprise.context.*;
In web.xml
<listener-class>liquibase.integration.servlet.LiquibaseServletListener</listener-class>
<listener-class>com.google.inject.servlet.GuiceServletContextListener</listener-class>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
Prerequisites
Before invoking this skill, ensure you have invoked:
-
jakarta-java25-migration - Use the jakarta-java25-migration skill
- Java 25 and Jakarta EE 10 migration must be done first
- REQUIRED before this skill
-
sdk-replacement - Use the sdk-replacement skill
- SAP Cloud SDK setup must be done first
- REQUIRED before this skill
Transformation Steps
Step 1: Schema Migration Tools (Conditional)
This step is conditional. Only apply if schema migration tools are detected.
Detect
find . -name "pom.xml" -not -path "*/target/*" -exec grep -l "liquibase" {} \;
find . -name "pom.xml" -not -path "*/target/*" -exec grep -l "flyway" {} \;
If either returns output, apply the relevant fix below.
1a: Liquibase — Upgrade to 4.x for Java 25 + HANA Cloud
Liquibase 3.x has two critical issues on CF:
-
JAXB dependency — Liquibase 3.x internally uses javax.xml.bind for parsing XML changelogs. Java 25 removed javax.xml.bind from the JDK, causing:
NoClassDefFoundError: javax/xml/bind/annotation/XmlElement
-
Unknown database: HDB — Liquibase 3.x doesn't recognize HANA Cloud's database identifier. It falls back to UnsupportedDatabase, which can fail on schema-specific operations.
Fix: Upgrade Liquibase to 4.x and add the HANA extension.
Note: Check Maven Central for the latest 4.x version at the time of migration.
Before:
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>3.8.2</version>
</dependency>
<dependency>
<groupId>com.github.lbitonti</groupId>
<artifactId>liquibase-hana</artifactId>
<version>3.0</version>
</dependency>
After:
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>4.30.0</version>
</dependency>
<dependency>
<groupId>org.liquibase.ext</groupId>
<artifactId>liquibase-hanadb</artifactId>
<version>4.30.0</version>
</dependency>
<dependency>
<groupId>com.mattbertolini</groupId>
<artifactId>liquibase-slf4j</artifactId>
<version>5.1.0</version>
</dependency>
Important: Remove the old com.github.lbitonti:liquibase-hana dependency if present (even if commented out). It is incompatible with Liquibase 4.x. See the Liquibase HANA Extension for details.
1b: Flyway — Upgrade for Jakarta EE 10 + HANA Cloud
Flyway 9.x+ supports Jakarta EE and HANA Cloud natively.
Note: Flyway 10.x requires Java 17+. Check Flyway docs for the latest version.
find . -name "pom.xml" -not -path "*/target/*" -exec grep -A1 "flyway" {} \;
If Flyway version is < 9.0, upgrade:
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>10.20.1</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-saphana</artifactId>
<version>10.20.1</version>
</dependency>
Step 2: DI Framework Integration (Conditional)
This step is conditional. Only apply if a DI framework is detected.
Detect
grep -r "com.google.inject\|GuiceServletContextListener\|GuiceFilter" --include="*.java" .
find . -name "pom.xml" -not -path "*/target/*" -exec grep -l "guice" {} \;
grep -r "javax.enterprise.context\|jakarta.enterprise.context\|@RequestScoped\|@ApplicationScoped" --include="*.java" .
find . -name "pom.xml" -not -path "*/target/*" -exec grep -l "weld" {} \;
find . -name "pom.xml" -not -path "*/target/*" -exec grep -l "hk2" {} \;
If any commands return output, check for the common issues below.
2a: Listener Ordering with Schema Migration Tools
If the application uses both a DI framework and a schema migration tool (Liquibase, Flyway), listener ordering in web.xml becomes critical.
Problem: Schema migration tools like Liquibase's LiquibaseServletListener perform their own JNDI lookup for the DataSource. This doesn't work when:
- The
DataSource is provided by a DI framework that must initialize first
- The DI container manages database connections
The required initialization order in web.xml is:
1. DI Framework Listener → Initializes DI container, creates DataSource
2. Schema Migration Listener → Gets DataSource from DI, runs migrations
3. Application Listener → Application starts with migrated schema
Solution: Replace Liquibase's built-in LiquibaseServletListener (or Flyway's equivalent) with a custom ServletContextListener that retrieves the DataSource from your DI container.
web.xml listener ordering:
<listener>
<listener-class>com.example.app.GuiceContextListener</listener-class>
</listener>
<listener>
<listener-class>com.example.app.CFLiquibaseRunner</listener-class>
</listener>
<listener>
<listener-class>com.example.app.AppInitializer</listener-class>
</listener>
Custom migration runner template (adapt to your DI framework):
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import javax.sql.DataSource;
import liquibase.Liquibase;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import liquibase.resource.ClassLoaderResourceAccessor;
import java.sql.Connection;
public class CFLiquibaseRunner implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
try {
DataSource dataSource = getDataSourceFromDI(sce);
try (Connection conn = dataSource.getConnection()) {
Database database = DatabaseFactory.getInstance()
.findCorrectDatabaseImplementation(new JdbcConnection(conn));
try (Liquibase liquibase = new Liquibase(
"db/changelog/db.changelog-master.xml",
new ClassLoaderResourceAccessor(),
database)) {
liquibase.update("");
}
}
} catch (Exception e) {
throw new RuntimeException("Liquibase migration failed", e);
}
}
private DataSource getDataSourceFromDI(ServletContextEvent sce) {
throw new UnsupportedOperationException("Adapt this method to your DI framework");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
Note: This is a template — you must adapt getDataSourceFromDI() to your specific DI framework. The key principle is: let the DI container manage the DataSource, and have the migration runner retrieve it from the container rather than doing its own JNDI lookup.
2b: Jersey + CDI (Weld) on Tomcat — Missing Bean Discovery
When using Jersey with CDI (Weld) on Tomcat (not TomEE), the jersey-cdi2-se module bootstraps Weld in SE (standalone) mode. Weld SE scans for META-INF/beans.xml inside classpath entries to discover bean archives. However, a WAR's standard CDI descriptor at WEB-INF/beans.xml is not recognized by Weld SE — only by Weld Servlet.
Symptom: All @Inject points fail at deployment with:
WELD-001408: Unsatisfied dependencies for type UserDAO with qualifiers @Default
WELD-001408: Unsatisfied dependencies for type Notifier with qualifiers @Default
...
The errors appear for every injected bean even though WEB-INF/beans.xml exists with bean-discovery-mode="all".
Detection:
grep -l "jersey-cdi2-se" pom.xml
grep -l "weld-servlet" pom.xml
grep -l "cf-tomcat-bom" pom.xml
ls src/main/resources/META-INF/beans.xml 2>/dev/null
If all three dependencies are present AND src/main/resources/META-INF/beans.xml does NOT exist, apply this fix.
Fix: Add a beans.xml to src/main/resources/META-INF/ so it is packaged as WEB-INF/classes/META-INF/beans.xml in the WAR. This makes Weld SE treat the classes directory as a bean archive:
mkdir -p src/main/resources/META-INF
Create src/main/resources/META-INF/beans.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd"
bean-discovery-mode="all"
version="4.0">
</beans>
Note: Keep the existing WEB-INF/beans.xml as well — it is still needed for Weld Servlet mode. The WAR should contain BOTH WEB-INF/beans.xml and WEB-INF/classes/META-INF/beans.xml. This issue does NOT affect TomEE-based applications because TomEE provides its own CDI container (OpenWebBeans) and does not use jersey-cdi2-se.
2c: Jersey + CDI (Weld) on Tomcat — Ambiguous HttpServletRequest
After fixing the bean discovery issue (Step 2b), a second deployment error can occur: WELD-001409: Ambiguous dependencies for HttpServletRequest. With bean-discovery-mode="all", Weld SE discovers multiple providers for HttpServletRequest — typically two Jersey SupplierBeanBridge instances and one Weld built-in bean.
Symptom:
WELD-001409: Ambiguous dependencies for type HttpServletRequest with qualifiers @Default
at injection point [BackedAnnotatedField] @Inject private com.example.filter.MyFilter.request
Possible dependencies:
- org.glassfish.jersey.ext.cdi1x.internal.CdiComponentProvider$SupplierBeanBridge
- org.glassfish.jersey.ext.cdi1x.internal.CdiComponentProvider$SupplierBeanBridge
- Built-in Bean
Cause: JAX-RS filters and resource classes that use @Inject (CDI) for HttpServletRequest instead of @Context (JAX-RS). When both Jersey and Weld can provide HttpServletRequest, CDI cannot resolve the ambiguity.
Detection:
grep -rn "@Inject" --include="*.java" -A1 . | grep "HttpServletRequest"
Fix: Changing @Inject to @Context resolves this specific ambiguity error, but will lead to a runtime failure (WELD-000710) when the proxy is actually invoked. See Step 2e for the complete solution that removes servlet type field injection entirely.
Note: Only change HttpServletRequest injection. Other CDI beans (DAOs, services) must remain @Inject. This issue does NOT affect TomEE-based applications.
2d: Jersey + CDI (Weld) on Tomcat — Ambiguous Configuration from JAX-RS Application
After fixing bean discovery (Step 2b) and HttpServletRequest ambiguity (Step 2c), a third deployment error can occur: WELD-001409: Ambiguous dependencies for Configuration. With bean-discovery-mode="all", Weld SE discovers the JAX-RS Application subclass (typically extending ResourceConfig) as a CDI managed bean. Since ResourceConfig implements jakarta.ws.rs.core.Configuration, it conflicts with Jersey's own InstanceBean that also provides Configuration.
Symptom:
WELD-001409: Ambiguous dependencies for type Configuration with qualifiers @Default
at injection point [BackedAnnotatedParameter] Parameter 2 of [BackedAnnotatedConstructor] @Inject public org.glassfish.jersey.internal.inject.ParamConverters$AggregatedProvider(@Context InjectionManager, @Context Configuration)
Possible dependencies:
- org.glassfish.jersey.inject.cdi.se.bean.InstanceBean
- Managed Bean [class com.example.rest.MyApplication] with qualifiers [@Any @Default]
Detection:
grep -rn "extends ResourceConfig\|extends Application" --include="*.java" src/
grep -B5 "extends ResourceConfig\|extends Application" --include="*.java" src/ | grep -c "Vetoed"
Fix: Add @jakarta.enterprise.inject.Vetoed to the Application subclass to exclude it from CDI bean discovery:
Before:
import org.glassfish.jersey.server.ResourceConfig;
public class MyApplication extends ResourceConfig {
After:
import jakarta.enterprise.inject.Vetoed;
import org.glassfish.jersey.server.ResourceConfig;
@Vetoed
public class MyApplication extends ResourceConfig {
Note: The @Vetoed annotation tells Weld to completely ignore this class during bean discovery. Jersey manages its own Application instance — it should never be a CDI managed bean. This issue does NOT affect TomEE-based applications.
2e: Jersey + CDI (Weld) on Tomcat — WELD-000710: Cannot Inject Servlet Types
After fixing the HttpServletRequest ambiguity (Step 2c) and Configuration ambiguity (Step 2d), a runtime error occurs when any code invokes a method on an injected HttpServletRequest, HttpServletResponse, or HttpSession. The @Context annotation resolves the CDI ambiguity, but the underlying Weld SE proxy still cannot provide the actual servlet object.
Symptom:
WELD-000710: Cannot inject HttpServletRequest outside of a Servlet request
at com.example.filter.MyFilter.getCurrentUser(MyFilter.java:85)
This error occurs at request time (not deployment), when the proxy created by @Context HttpServletRequest tries to delegate to Weld SE's built-in HttpServletRequestBean, which requires an active servlet request context that Weld SE does not have.
Root cause: jersey-cdi2-se redirects all @Context injection through CDI. For servlet types (HttpServletRequest, HttpServletResponse, HttpSession), Weld's built-in beans take precedence over Jersey's CDI extension beans. These built-in beans only work in Weld Servlet mode, not in Weld SE mode. The same issue affects HttpServletResponse and HttpSession.
Detection:
grep -rn "@Context" --include="*.java" -A1 src/ | grep -E "HttpServletRequest|HttpServletResponse|HttpSession"
Fix: Remove all field-level @Context injection of servlet types. Replace with JAX-RS alternatives depending on the class type:
For ContainerRequestFilter classes — use ContainerRequestContext (passed to the filter() method):
Before:
public class MyFilter implements ContainerRequestFilter {
@Context
private HttpServletRequest request;
@Override
public void filter(ContainerRequestContext requestContext) {
String userId = request.getUserPrincipal().getName();
HttpSession session = request.getSession();
User user = (User) session.getAttribute("user");
}
}
After:
public class MyFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) {
String userId = requestContext.getSecurityContext().getUserPrincipal().getName();
User user = resolveUser(userId);
requestContext.setProperty("user", user);
}
For JAX-RS resource classes (@Path) — replace @Context HttpServletRequest with @Context SecurityContext (a JAX-RS type, not a servlet type — Weld SE does not intercept it):
Before:
@RequestScoped
@Path("api/v1")
public class MyService {
@Context
private HttpServletRequest httpRequest;
@GET @Path("/data")
public Response getData() {
String userId = httpRequest.getUserPrincipal().getName();
boolean isAdmin = httpRequest.isUserInRole("Admin");
}
}
After:
@RequestScoped
@Path("api/v1")
public class MyService {
@Context
private SecurityContext securityContext;
@GET @Path("/data")
public Response getData() {
String userId = securityContext.getUserPrincipal().getName();
boolean isAdmin = securityContext.isUserInRole("Admin");
}
}
For HttpServletResponse — move to method parameter injection (resolved by Jersey at invocation time, not by CDI):
Before:
@Context
private HttpServletResponse httpResponse;
@GET @Path("/report")
public HttpServletResponse getReport() {
httpResponse.setContentType("text/csv");
return httpResponse;
}
After:
@GET @Path("/report")
public HttpServletResponse getReport(@Context HttpServletResponse httpResponse) {
httpResponse.setContentType("text/csv");
return httpResponse;
}
For non-resource CDI beans (factories, utilities) — pass the needed values from callers instead of injecting HttpServletRequest:
Before:
@RequestScoped
public class MyFactory {
@Context
private HttpServletRequest httpRequest;
public void doWork() {
boolean isAdmin = httpRequest.isUserInRole("Admin");
}
}
After:
@RequestScoped
public class MyFactory {
public void doWork(boolean isAdmin) {
// use isAdmin directly — no field injection needed
}
}
**For `IdentityAdapter` or similar classes** that accept `HttpServletRequest` as a method parameter to extract user info — change to accept `java.security.Principal`:
**Before:**
```java
public String getUserName(HttpServletRequest request) {
Principal principal = request.getUserPrincipal();
// extract claims from principal...
}
After:
public String getUserName(Principal principal) {
}
Key insight: jakarta.ws.rs.core.SecurityContext is a JAX-RS type, NOT a servlet type. Weld SE does not have a built-in bean for it, so Jersey's CDI extension provides it correctly. The affected servlet types are: HttpServletRequest, HttpServletResponse, HttpSession, and ServletContext. This issue does NOT affect TomEE-based applications.
2f: Guice javax → jakarta Namespace
If the application uses Google Guice, check the version:
find . -name "pom.xml" -not -path "*/target/*" -exec grep -A1 "guice" {} \;
Guice 7.x+ supports jakarta.inject and jakarta.servlet. If on Guice < 7:
Note: Guice 7.x is a breaking change from Guice 4.x/5.x — it requires jakarta.inject instead of javax.inject. Since the jakarta-java25-migration skill already migrates javax.inject → jakarta.inject, Guice 7.x is the correct target. See the Guice 7.0 Release Notes for the full change list.
Before:
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>4.2.3</version>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-servlet</artifactId>
<version>4.2.3</version>
</dependency>
After:
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>7.0.0</version>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-servlet</artifactId>
<version>7.0.0</version>
</dependency>
2g: Jersey + CDI (Weld) on Tomcat — WELD-001303: @SessionScoped Not Available
After fixing WELD-000710 (Step 2e), a runtime error can occur if any CDI bean uses @SessionScoped. Weld SE mode does not provide an active session context, so any @SessionScoped bean will fail when its proxy is first accessed.
Symptom:
WELD-001303: No active contexts for scope type jakarta.enterprise.context.SessionScoped
at com.example.adapter.IdentityAdapter$Proxy$_$$_WeldClientProxy.getUserName()
This error occurs at request time when the CDI proxy for a @SessionScoped bean tries to find an active session context.
Root cause: jersey-cdi2-se runs Weld in SE mode, which does not activate the session scope. On Neo with a full servlet container, @SessionScoped works because the servlet container manages HTTP sessions. On CF with Weld SE, there is no servlet session lifecycle.
Detection:
grep -rn "@SessionScoped" --include="*.java" src/
Fix: Change @SessionScoped to an appropriate alternative scope:
@ApplicationScoped — for stateless services, adapters, and utilities that don't hold per-user state. This is the most common replacement.
@RequestScoped — for beans that need per-request isolation but not cross-request session state.
Also remove Serializable interface and serialVersionUID if they were only there for session serialization.
Before:
@SessionScoped
public class IdentityAdapter implements Serializable {
private static final long serialVersionUID = 1L;
}
After:
@ApplicationScoped
public class IdentityAdapter {
}
Key insight: If the bean was using @SessionScoped to cache user-specific data across requests (e.g., storing user info in a session), replace that caching with per-request property storage via ContainerRequestContext.setProperty()/getProperty() (see Step 2e) or pass the values explicitly from the caller. This issue does NOT affect TomEE-based applications.
Step 3: Known Library Compatibility Table
For other third-party libraries found during detection, check this table:
| Library | Breaking Version | Minimum Compatible Version | Issue | Fix |
|---|
| Apache POI (OOXML) | 3.x, 4.x | 4.x (with JAXB deps) or 5.x | Uses javax.xml.bind internally for OOXML parsing | Add jaxb-api + jaxb-impl at appropriate scope (see jakarta-java25-migration Step 10) or upgrade to POI 5.x |
| iText | 5.x | 7.x+ (or OpenPDF 1.3+) | iText 5.x uses removed Java APIs | Upgrade or switch to OpenPDF |
| EhCache | 2.x | 3.10+ | javax.cache namespace | Upgrade to EhCache 3.x with jakarta.cache |
| Hazelcast | < 5.0 | 5.0+ | javax.cache namespace | Upgrade to 5.x |
| Quartz Scheduler | 2.3.x | 2.5.0+ | Java 25 reflection issues | Upgrade to 2.5.x+ |
| Retrofit | 1.x | 2.9+ | OkHttp 3.x incompatibility | Upgrade to Retrofit 2.x |
| Jackson | < 2.13 | 2.15+ | Java 25 module access warnings | Upgrade to 2.15+ |
| Hibernate | 5.x | 6.x+ | javax.persistence namespace | Upgrade to Hibernate 6.x (uses jakarta.persistence) |
| Log4j | 1.x | 2.x (Log4j2) | EOL, security vulnerabilities, Java 25 issues | Migrate to Log4j2 or SLF4J + Logback |
| ModelMapper | 2.x, 3.x | 3.x (with fix) | PropertyMap.configure() proxy NPE on enum fields with Java 25 | Replace PropertyMap with Converter for mappings that access enum fields (see Common Issues) |
| EclipseLink (MOXy) | 4.x | 4.x (with deps) | MOXy JSON provider needs jakarta.json-api not bundled transitively | Add jakarta.json-api + parsson (Eclipse JSON-P impl) to pom.xml (see Common Issues) |
Note: This table covers commonly encountered libraries. For libraries not listed, check their release notes for Java 25 and Jakarta EE 10 compatibility.
Step 4: Generic Compatibility Check
For any dependency not in the known table above:
mvn clean package -DskipTests
jar tf target/*.war | grep "WEB-INF/lib/" | sort
Heuristic: If a library's latest version was released before 2021, it likely has Java 25 or Jakarta EE issues. Check the library's documentation for migration guides.
For each flagged library:
- Check if a Jakarta-compatible version exists → upgrade
- If no compatible version exists → check for an alternative library
- If no alternative exists → add bridge dependencies (
javax.xml.bind, etc.) at the appropriate scope
Configuration Files
No new configuration files are created by this skill. Changes are limited to pom.xml dependency updates, web.xml listener ordering, and optional custom Java classes.
CF Services
No CF services required for this skill.
Verification
1. Compile Check
mvn clean compile
2. Test Compile Check
mvn test-compile
3. Run Tests
mvn test
4. Check for Remaining javax.xml.bind Runtime Issues
mvn dependency:tree | grep -i "jaxb\|xml.bind"
Common Issues
Issue: NoClassDefFoundError: javax/xml/bind after Liquibase upgrade
Cause: Another library (not Liquibase) still depends on javax.xml.bind.
Solution: Run mvn dependency:tree | grep -B5 "xml.bind" to find which dependency pulls it. Add jaxb-api + jaxb-impl at the appropriate scope, or upgrade the offending library.
Issue: Liquibase "Unknown database: HDB"
Cause: Missing liquibase-hanadb extension.
Solution: Add org.liquibase.ext:liquibase-hanadb at the same version as liquibase-core.
Issue: DI container not initialized when Liquibase runs
Cause: Listener ordering in web.xml — Liquibase listener runs before DI framework listener.
Solution: Reorder listeners in web.xml: DI framework first, then schema migration, then application init. See Step 2a above.
Issue: WELD-001408 Unsatisfied dependencies for all beans on Tomcat with Jersey
Cause: jersey-cdi2-se bootstraps Weld SE which looks for META-INF/beans.xml in classpath entries, not WEB-INF/beans.xml.
Solution: Add src/main/resources/META-INF/beans.xml with bean-discovery-mode="all". See Step 2b above.
Issue: WELD-001409 Ambiguous dependencies for HttpServletRequest after adding META-INF/beans.xml
Cause: With bean-discovery-mode="all", both Jersey and Weld provide HttpServletRequest as CDI beans, creating ambiguity for @Inject injection points.
Solution: Change @Inject to @Context for HttpServletRequest in JAX-RS filters and resource classes. See Step 2c above.
Issue: WELD-001409 Ambiguous dependencies for Configuration after adding META-INF/beans.xml
Cause: With bean-discovery-mode="all", the JAX-RS Application subclass (extending ResourceConfig) is discovered as a CDI bean that implements Configuration, conflicting with Jersey's own InstanceBean.
Solution: Add @jakarta.enterprise.inject.Vetoed to the Application subclass. See Step 2d above.
Issue: WELD-000710 Cannot inject HttpServletRequest outside of a Servlet request
Cause: jersey-cdi2-se runs Weld in SE mode. Weld SE's built-in beans for servlet types (HttpServletRequest, HttpServletResponse, HttpSession) require an active servlet request context that doesn't exist in SE mode. This occurs even with @Context (not just @Inject) because jersey-cdi2-se routes all injection through CDI.
Solution: Remove all field-level @Context/@Inject of servlet types. Use @Context SecurityContext (JAX-RS type) for principal/role access, ContainerRequestContext in filters, and method parameter @Context for HttpServletResponse. See Step 2e above.
Issue: WELD-001303 No active contexts for scope type SessionScoped
Cause: jersey-cdi2-se runs Weld in SE mode, which does not activate the session scope. Any @SessionScoped bean proxy will throw this error when first accessed at request time.
Solution: Change @SessionScoped to @ApplicationScoped (for stateless beans) or @RequestScoped (for per-request beans). Remove Serializable interface if it was only there for session serialization. See Step 2g above.
Issue: ModelMapper PropertyMap NullPointerException on enum fields with Java 25
Cause: ModelMapper's PropertyMap.configure() creates CGLIB/ByteBuddy proxies to record property access. When a getter returns an enum type (e.g., getType() returns BenefitType), the proxy returns null because enums cannot be proxied. Calling .toString() on the null enum causes an NPE. This manifests as NullPointerException: Cannot invoke "MyEnum.toString()" because the return value of "MyEntity.getType()" is null during PropertyMap.configure(), not during actual mapping.
Solution: Replace the PropertyMap with a Converter (using AbstractConverter) that maps objects manually at mapping time instead of at configuration time. In the converter, access the real source object's enum field directly. Update callers from mapper.addMappings(propertyMap) to mapper.addConverter(converter).
Before:
public static PropertyMap<MyEntity, MyDTO> entityMap = new PropertyMap<>() {
protected void configure() {
map().setName(source.getName());
String typeStr = source.getType().toString();
map().setType(typeStr);
}
};
After:
public static Converter<MyEntity, MyDTO> entityConverter = new AbstractConverter<>() {
protected MyDTO convert(MyEntity source) {
MyDTO dto = new MyDTO();
dto.setName(source.getName());
dto.setType(source.getType() != null ? source.getType().toString() : null);
return dto;
}
};
Issue: NoClassDefFoundError: jakarta/json/JsonException with EclipseLink MOXy 4.x
Cause: EclipseLink MOXy 4.x (used as the JSON provider via MOXyJsonProvider) requires Jakarta JSON Processing (JSON-P) API at runtime for JSON deserialization. The eclipselink all-in-one artifact does not transitively include jakarta.json-api. This manifests as ClassNotFoundException: jakarta.json.JsonException during JSON request/response processing (e.g., POST with JSON body).
Solution: Add both the Jakarta JSON-P API and an implementation to pom.xml:
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.eclipse.parsson</groupId>
<artifactId>parsson</artifactId>
<version>1.1.5</version>
</dependency>
Issue: Guice "No implementation for jakarta.servlet.http.HttpServletRequest" after migration
Cause: Guice version doesn't support jakarta.servlet namespace.
Solution: Upgrade Guice to 7.x. See Step 2f above.
Issue: Library compiled against javax.servlet won't link
Cause: Library uses javax.servlet.* but the container provides jakarta.servlet.*.
Solution: Check for a Jakarta-compatible version. If not available, the library must be replaced or wrapped.
Next Steps
After completing this skill, proceed to the remaining feature skills: