- 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
```xml
<!-- Schema migration tools -->
<artifactId>liquibase-core</artifactId>
<artifactId>flyway-core</artifactId>
<!-- DI frameworks -->
<artifactId>guice</artifactId>
<artifactId>guice-servlet</artifactId>
<artifactId>weld-servlet-core</artifactId>
<!-- Known problematic libraries -->
<artifactId>poi-ooxml</artifactId>
<artifactId>itextpdf</artifactId>
<artifactId>ehcache</artifactId>
<artifactId>hazelcast</artifactId>
<artifactId>quartz</artifactId>
<artifactId>retrofit</artifactId>
```
### In Java source files
```java
// DI framework imports
import com.google.inject.*;
import com.google.inject.servlet.GuiceServletContextListener;
// Liquibase programmatic usage
import liquibase.Liquibase;
import liquibase.integration.servlet.LiquibaseServletListener;
// CDI annotations
import javax.enterprise.context.*;
import jakarta.enterprise.context.*;
```
### In web.xml
```xml
<!-- Liquibase listener -->
<listener-class>liquibase.integration.servlet.LiquibaseServletListener</listener-class>
<!-- Guice filter/listener -->
<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:
1. **jakarta-java25-migration** - `Use the jakarta-java25-migration skill`
- Java 25 and Jakarta EE 10 migration must be done first
- REQUIRED before this skill
2. **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
```bash
# Liquibase
find . -name "pom.xml" -not -path "*/target/*" -exec grep -l "liquibase" {} \;
# Flyway
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:
1. **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
```
2. **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](https://mvnrepository.com/artifact/org.liquibase/liquibase-core) for the latest 4.x version at the time of migration.
**Before:**
```xml
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>3.8.2</version> <!-- or any 3.x -->
</dependency>
<!-- Old HANA community extension (may be present or commented out) -->
<dependency>
<groupId>com.github.lbitonti</groupId>
<artifactId>liquibase-hana</artifactId>
<version>3.0</version>
</dependency>
```
**After:**
```xml
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>4.30.0</version>
</dependency>
<!-- Official HANA extension for Liquibase 4.x -->
<dependency>
<groupId>org.liquibase.ext</groupId>
<artifactId>liquibase-hanadb</artifactId>
<version>4.30.0</version>
</dependency>
<!-- SLF4J bridge for Liquibase logging -->
<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](https://github.com/liquibase/liquibase-hanadb) 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](https://documentation.red-gate.com/fd/release-notes-for-flyway-engine-179732572.html) for the latest version.
```bash
# Check current Flyway version
find . -name "pom.xml" -not -path "*/target/*" -exec grep -A1 "flyway" {} \;
```
If Flyway version is < 9.0, upgrade:
```xml
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>10.20.1</version>
</dependency>
<!-- For HANA support -->
<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
```bash
# Google Guice
grep -r "com.google.inject\|GuiceServletContextListener\|GuiceFilter" --include="*.java" .
find . -name "pom.xml" -not -path "*/target/*" -exec grep -l "guice" {} \;
# CDI (Weld)
grep -r "javax.enterprise.context\|jakarta.enterprise.context\|@RequestScoped\|@ApplicationScoped" --include="*.java" .
find . -name "pom.xml" -not -path "*/target/*" -exec grep -l "weld" {} \;
# HK2
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:**
```xml
<!-- Order matters! DI must initialize before schema migration -->
<listener>
<!-- 1. DI framework initializes first -->
<listener-class>com.example.app.GuiceContextListener</listener-class>
</listener>
<listener>
<!-- 2. Schema migration runs after DI provides the DataSource -->
<listener-class>com.example.app.CFLiquibaseRunner</listener-class>
</listener>
<listener>
<!-- 3. Application initializes after schema is ready -->
<listener-class>com.example.app.AppInitializer</listener-class>
</listener>
```
**Custom migration runner template (adapt to your DI framework):**
```java
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;
/**
* Custom Liquibase runner that retrieves the DataSource from the DI container
* instead of performing a direct JNDI lookup.
*
* ADAPT: Replace getDataSourceFromDI() with your DI framework's lookup method.
*/
public class CFLiquibaseRunner implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
try {
// ADAPT: Get DataSource from your DI framework
// For Guice: Injector injector = (Injector) sce.getServletContext().getAttribute(Injector.class.getName());
// DataSource ds = injector.getInstance(DataSource.class);
// For CDI: DataSource ds = CDI.current().select(DataSource.class).get();
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) {
// TODO: Implement based on your DI framework
throw new UnsupportedOperationException("Adapt this method to your DI framework");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// no-op
}
}
```
> **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:**
```bash
# Check if using jersey-cdi2-se with Weld on Tomcat
grep -l "jersey-cdi2-se" pom.xml
grep -l "weld-servlet" pom.xml
grep -l "cf-tomcat-bom" pom.xml
# Check if META-INF/beans.xml exists in resources
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:
```bash
mkdir -p src/main/resources/META-INF
```
Create `src/main/resources/META-INF/beans.xml`:
```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:
Ver no GitHub