Configures TomEE container for EJB applications in Cloud Foundry Neo migrations. Detects @Stateless, @Singleton, @EJB annotations, javax.ejb/jakarta.ejb imports, or neo-javaee7-wp-api dependency.
npx claudepluginhub sap-samples/btp-neo-java-app-migration --plugin sap-btp-neo-migrationThis skill is limited to using the following tools:
Configure TomEE container for JavaEE/EJB applications in Cloud Foundry.
Guides Next.js Cache Components and Partial Prerendering (PPR): 'use cache' directives, cacheLife(), cacheTag(), revalidateTag() for caching, invalidation, static/dynamic optimization. Auto-activates on cacheComponents: true.
Processes PDFs: extracts text/tables/images, merges/splits/rotates pages, adds watermarks, creates/fills forms, encrypts/decrypts, OCRs scans. Activates on PDF mentions or output requests.
Share bugs, ideas, or general feedback.
Configure TomEE container for JavaEE/EJB applications in Cloud Foundry.
Migrate Neo applications using TomEE runtime (with EJB support) to Cloud Foundry's TomEE 10 container instead of Tomcat.
This skill applies if any of these patterns are found:
<dependency>
<groupId>com.sap.cloud</groupId>
<artifactId>neo-javaee7-wp-api</artifactId>
</dependency>
import javax.ejb.Stateless;
import javax.ejb.Singleton;
import javax.ejb.EJB;
import javax.ejb.Schedule;
@Stateless
public class MyService { }
@Singleton
public class MySingleton { }
<!-- EJB references -->
<ejb-local-ref>
<ejb-ref-name>ejb/MyService</ejb-ref-name>
<local>com.example.MyService</local>
</ejb-local-ref>
Working directory: This skill must run inside the
-cf-migrationcopy of your app, created byjakarta-java25-migrationorneo-to-cf-migration-orchestrator. If your current directory does not end in-cf-migration, switch to it before proceeding.
Before invoking this skill, ensure you have invoked:
Use the jakarta-java25-migration skill
Note: TomEE is an alternative to the Tomcat migration path. Choose TomEE if your application uses EJB features.
Before (Neo):
<dependency>
<groupId>com.sap.cloud</groupId>
<artifactId>neo-javaee7-wp-api</artifactId>
<version>${neo.version}</version>
<scope>provided</scope>
</dependency>
After (Cloud Foundry TomEE):
See assets/pom-cf-tomee.xml for a complete template.
Key changes:
<dependencyManagement>
<dependencies>
<!-- CF TomEE BOM instead of cf-tomcat-bom -->
<dependency>
<groupId>com.sap.cloud.sjb.cf</groupId>
<artifactId>cf-tomee-bom</artifactId>
<version>${cf-tomee-bom-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.sap.cloud.sdk</groupId>
<artifactId>sdk-modules-bom</artifactId>
<version>${sdk-modules-bom-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<cf-tomee-bom-version>2.25.0</cf-tomee-bom-version>
<sdk-modules-bom-version>5.14.0</sdk-modules-bom-version>
<jakarta.persistence-api-version>3.2.0</jakarta.persistence-api-version>
</properties>
<dependencies>
<dependency>
<groupId>com.sap.cloud.sdk.cloudplatform</groupId>
<artifactId>scp-cf</artifactId>
</dependency>
<!-- Jakarta EE API provided by TomEE -->
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>10.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
Before (Java EE 7):
import javax.ejb.Stateless;
import javax.ejb.EJB;
import javax.ejb.Schedule;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.annotation.PostConstruct;
@Stateless
public class MyBusinessService {
@EJB
private OtherService otherService;
public String doWork() {
return otherService.process();
}
}
@Singleton
@Startup
public class ApplicationInitializer {
@PostConstruct
public void init() {
System.out.println("Application started");
}
@Schedule(hour = "*", minute = "*/5", persistent = false)
public void scheduledTask() {
System.out.println("Running scheduled task");
}
}
After (Jakarta EE 10):
import jakarta.ejb.Stateless;
import jakarta.ejb.EJB;
import jakarta.ejb.Schedule;
import jakarta.ejb.Singleton;
import jakarta.ejb.Startup;
import jakarta.annotation.PostConstruct;
@Stateless
public class MyBusinessService {
@EJB
private OtherService otherService;
public String doWork() {
return otherService.process();
}
}
@Singleton
@Startup
public class ApplicationInitializer {
@PostConstruct
public void init() {
System.out.println("Application started");
}
@Schedule(hour = "*", minute = "*/5", persistent = false)
public void scheduledTask() {
System.out.println("Running scheduled task");
}
}
For TomEE, use resources.xml instead of Tomcat's context.xml.
Create src/main/webapp/WEB-INF/resources.xml:
<?xml version='1.0' encoding='utf-8'?>
<resources>
<Resource id="jdbc/DefaultDB"
provider="xs.openejb:XS Default JDBC Database"
type="javax.sql.DataSource">
service=${service_name_for_DefaultDB}
</Resource>
</resources>
Note: TomEE uses
xs.openejb:XS Default JDBC Databaseprovider instead of Tomcat'sTomcatDataSourceFactory. Theserviceplaceholder is specified as element content, not as an XML attribute.
If the application uses JPA, the persistence.xml must be updated for TomEE's JTA environment. There are four critical changes:
persistence.xml from src/main/resources/META-INF/ to src/main/webapp/META-INF/. TomEE processes DDL generation correctly only when the persistence.xml is at the WAR root META-INF/ location, not at WEB-INF/classes/META-INF/.<provider>, EclipseLink may not be properly initialized as the JPA provider.jdbc/DefaultDB without the java:comp/env/ prefix.NVARCHAR to 5000 characters. Any @Lob field with length > 5000 must use columnDefinition = "NCLOB" instead of length, otherwise DDL generation silently fails for those tables.Before (Neo):
<!-- Location: src/main/resources/META-INF/persistence.xml -->
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="MyApp" transaction-type="JTA">
<jta-data-source>java:comp/env/jdbc/DefaultDB</jta-data-source>
<class>com.example.MyEntity</class>
<properties>
<property name="eclipselink.ddl-generation" value="create-tables" />
<property name="eclipselink.ddl-generation.output-mode" value="database" />
</properties>
</persistence-unit>
</persistence>
After (Cloud Foundry TomEE):
<!-- Location: src/main/webapp/META-INF/persistence.xml (MUST be under webapp, not resources) -->
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence
https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
version="3.0">
<persistence-unit name="MyApp" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/DefaultDB</jta-data-source>
<class>com.example.MyEntity</class>
<properties>
<property name="eclipselink.ddl-generation" value="create-or-extend-tables" />
<property name="eclipselink.target-database" value="HANA"/>
</properties>
</persistence-unit>
</persistence>
Key changes:
src/main/resources/META-INF/ to src/main/webapp/META-INF/ — TomEE's DDL generation only runs correctly when persistence.xml is at the WAR root META-INF/ location. Placing it under resources/META-INF/ (which maps to WEB-INF/classes/META-INF/ in the WAR) causes DDL generation to silently fail.<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> — explicit EclipseLink provider (TomEE defaults to OpenJPA)<jta-data-source>jdbc/DefaultDB</jta-data-source> — short JNDI name without java:comp/env/ prefixeclipselink.ddl-generation=create-or-extend-tables — use this value instead of create-tables for robustness during schema evolutionWarning: If
persistence.xmlis placed undersrc/main/resources/META-INF/instead ofsrc/main/webapp/META-INF/, EclipseLink's DDL generation will silently fail in TomEE — no tables will be created and no error will be logged at startup. The failure only becomes visible at runtime when queries hit missing tables. Always verify by adding<property name="eclipselink.logging.level" value="FINE"/>temporarily and checking forCREATE TABLEstatements in the logs.
HANA Cloud enforces a maximum of 5000 characters for NVARCHAR columns. EclipseLink maps @Lob @Column(length = N) to NVARCHAR(N) — if N > 5000, the CREATE TABLE statement fails silently.
Before (works on Neo HANA on-premise, fails on HANA Cloud):
@Lob
@Column(nullable = false, length = 16 * 1024) // 16384 > 5000 limit
private String longText = "";
@Lob
@Column(length = 512 * 1024) // 524288 > 5000 limit
private String introduction = "";
After (works on HANA Cloud):
@Lob
@Column(nullable = false, columnDefinition = "NCLOB")
private String longText = "";
@Lob
@Column(columnDefinition = "NCLOB")
private String introduction = "";
Replace length = N with columnDefinition = "NCLOB" on every @Lob field where the original length exceeds 5000. Keep other @Column attributes (nullable, updatable) as-is.
Note:
NCLOBhas no practical size limit on HANA Cloud and is the correct type for large text fields. Fields withlength <= 5000(e.g.,@Column(length = 1024)) can remain asNVARCHARand do not need this change.
Create src/main/webapp/META-INF/sap_java_buildpack/config/resource_configuration.yml:
---
tomee/webapps/ROOT/WEB-INF/resources.xml:
service_name_for_DefaultDB: ${app-name}-hana
Note: The path is
tomee/webapps/ROOT/WEB-INFinstead oftomcat/webapps/ROOT.
_schema-version: "3.2"
ID: ${app-name}
version: 0.0.1
modules:
- name: ${app-name}
type: java.tomcat # Still use java.tomcat type
path: target/${app-name}.war
parameters:
buildpack: sap_java_buildpack_jakarta
disk-quota: 512MB
memory: 512MB
properties:
# Enable TomEE runtime
TARGET_RUNTIME: tomee
ENABLE_SECURITY_JAVA_API_V2: true
SET_LOGGING_LEVEL: 'ROOT: INFO'
# Resource configuration for TomEE path (YAML list format)
JBP_CONFIG_RESOURCE_CONFIGURATION:
- tomee/webapps/ROOT/WEB-INF/resources.xml:
service_name_for_DefaultDB: ${app-name}-hana
requires:
- name: ${app-name}-hana
resources:
- name: ${app-name}-hana
type: com.sap.xs.hana-schema
Key differences:
TARGET_RUNTIME: tomee property enables TomEE instead of TomcatJBP_CONFIG_RESOURCE_CONFIGURATION uses YAML list format (not inline string)tomee/webapps/ROOT/WEB-INF instead of tomcat/webapps/ROOTIf the Neo application declared CXFNonSpringJaxrsServlet in web.xml to configure JAX-RS, you must remove it when using TomEE. CXFNonSpringJaxrsServlet instantiates JAX-RS endpoint classes directly (via new), completely bypassing TomEE's CDI container. This means @Inject, @EJB, and @Resource fields in endpoint classes will remain null, causing NullPointerException at runtime.
TomEE has built-in CXF/JAX-RS integration that is CDI-aware. If your JAX-RS Application subclass has @ApplicationPath("/rest"), TomEE will auto-discover it and wire endpoints through the CDI container.
# Check for CXFNonSpringJaxrsServlet in web.xml
grep -n "CXFNonSpringJaxrsServlet" src/main/webapp/WEB-INF/web.xml
If found, apply the fix below.
Remove the entire <servlet> and <servlet-mapping> block for CXF from web.xml:
<!-- REMOVE: CXF servlet bypasses TomEE CDI injection -->
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>
org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet
</servlet-class>
<init-param>
<param-name>jakarta.ws.rs.Application</param-name>
<param-value>com.example.RestActivator</param-value>
</init-param>
<init-param>
<param-name>jaxrs.providers</param-name>
<param-value>com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
Move provider registration to the JAX-RS Application class:
If providers (e.g., Jackson) were registered via jaxrs.providers init-param, register them in the Application.getClasses() method instead:
```java
@ApplicationPath("/rest")
public class RestActivator extends Application {
private final Set<Class<?>> classes = new HashSet<>();
public RestActivator() {
classes.add(MyEndpoint.class);
classes.add(com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider.class); // <-- register providers here
}
@Override
public Set<Class<?>> getClasses() {
return classes;
}
}
> **Note:** This only applies to TomEE. On plain Tomcat (without built-in JAX-RS), `CXFNonSpringJaxrsServlet` is required because there is no container-managed CXF integration. If migrating to Tomcat instead of TomEE, keep the servlet but be aware that CDI injection will not work in endpoint classes.
### Step 8: Update beans.xml to Jakarta EE Namespace
If the project has a `beans.xml` with the old `javax` namespace, update it to Jakarta CDI 4.0. The old namespace may cause CDI bean discovery issues on TomEE 10.
**Before:**
```xml
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
After:
<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">
Note:
bean-discovery-mode="all"ensures all classes are discoverable as CDI beans, including those without explicit CDI annotations. This matches the implicit behavior of CDI 1.0 (beans_1_0.xsd).
Before (Neo-specific JNDI):
Context ctx = new InitialContext();
MyService service = (MyService) ctx.lookup("java:comp/env/ejb/MyService");
After (Standard Jakarta EE JNDI):
Context ctx = new InitialContext();
// Use standard Jakarta EE JNDI names
MyService service = (MyService) ctx.lookup("java:module/MyService");
// OR
MyService service = (MyService) ctx.lookup("java:global/${app-name}/MyService");
TomEE Timer Service works similarly to Neo, but configuration may differ:
import jakarta.ejb.Schedule;
import jakarta.ejb.Singleton;
import jakarta.ejb.Timer;
import jakarta.ejb.Timeout;
import jakarta.annotation.Resource;
import jakarta.ejb.TimerService;
@Singleton
public class ScheduledTasks {
@Resource
private TimerService timerService;
// Declarative timer
@Schedule(hour = "*", minute = "*/10", persistent = false)
public void periodicTask() {
// Runs every 10 minutes
}
// Programmatic timer
public void scheduleTask(long delay) {
timerService.createSingleActionTimer(delay, null);
}
@Timeout
public void handleTimeout(Timer timer) {
// Handle programmatic timer expiration
}
}
Note: Set
persistent = falsefor timers in Cloud Foundry as persistent timers require database storage.
src/main/
├── java/
│ └── com/example/
│ ├── ejb/
│ │ └── MyBusinessService.java
│ └── servlet/
│ └── MyServlet.java
└── webapp/
├── META-INF/
│ ├── persistence.xml # JPA config (MUST be here, not resources/META-INF)
│ └── sap_java_buildpack/
│ └── config/
│ └── resource_configuration.yml
└── WEB-INF/
├── resources.xml # TomEE data source config
├── web.xml
└── openejb-jar.xml (optional)
| File | Location | Purpose |
|---|---|---|
resources.xml | src/main/webapp/WEB-INF/ | TomEE data source configuration |
resource_configuration.yml | src/main/webapp/META-INF/sap_java_buildpack/config/ | Service name mapping |
openejb-jar.xml | src/main/webapp/WEB-INF/ | EJB deployment configuration |
Same services as Tomcat-based deployment, but with TomEE runtime.
mvn clean compile
mvn clean package
cf deploy . -f
cf logs ${app-name} --recent | grep -i tomee
# Should show TomEE startup messages
curl "https://${app-url}/api/service"
# Should execute EJB method
Cause: JNDI name mismatch. Solution: Use standard Jakarta EE JNDI names:
java:module/BeanNamejava:global/app/module/BeanNameCause: Persistent timer without database.
Solution: Set persistent = false on @Schedule.
Cause: Multiple beans implement same interface.
Solution: Use @EJB(beanName = "SpecificBean").
Cause: Incorrect interface casting. Solution: Ensure you're casting to the interface, not the implementation.
Cause: The resources.xml data source placeholder uses XML attribute syntax (service="${service_name_for_DefaultDB}") instead of element body text. The SAP Java Buildpack's placeholder substitution does not work with XML attributes — only with element body text.
Diagnosis: Stack trace shows NullPointerException: Cannot invoke "String.equals(Object)" because "serviceName" is null in VcapServices.getService().
Solution: Use element body text for the service placeholder in resources.xml:
<!-- WRONG — attribute syntax, placeholder not substituted -->
<Resource id="jdbc/DefaultDB"
provider="xs.openejb:XS Default JDBC Database"
type="javax.sql.DataSource"
service="${service_name_for_DefaultDB}" />
<!-- CORRECT — element body text, placeholder substituted -->
<Resource id="jdbc/DefaultDB"
provider="xs.openejb:XS Default JDBC Database"
type="javax.sql.DataSource">
service=${service_name_for_DefaultDB}
</Resource>
Also verify that the path in resource_configuration.yml and JBP_CONFIG_RESOURCE_CONFIGURATION matches the actual resources.xml location (tomee/webapps/ROOT/WEB-INF/resources.xml, not META-INF).
Cause: The persistence.xml is placed at src/main/resources/META-INF/ (packaged to WEB-INF/classes/META-INF/ in the WAR). TomEE's DDL generation only works correctly when persistence.xml is at the WAR root META-INF/ location (src/main/webapp/META-INF/). No error is logged at startup — the failure only surfaces at runtime as Could not find table/view errors.
Diagnosis: Application starts normally but all JPA queries fail with DatabaseException: invalid table name: Could not find table/view TABLENAME in schema. To confirm DDL is being attempted, add <property name="eclipselink.logging.level" value="FINE"/> and check logs for CREATE TABLE statements.
Solution: See Step 4 above. Move persistence.xml from src/main/resources/META-INF/ to src/main/webapp/META-INF/ and ensure all three configuration fixes are applied:
src/main/webapp/META-INF/persistence.xml<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider><jta-data-source>jdbc/DefaultDB</jta-data-source> (without java:comp/env/ prefix)Cause: HANA Cloud enforces a maximum of 5000 characters for NVARCHAR columns. Entity fields annotated with @Lob @Column(length = 16384) or similar cause EclipseLink to generate NVARCHAR(16384), which HANA rejects with error code 267: specified length too long for its datatype. EclipseLink logs this as a warning but continues — no tables are created for any entity that hits this limit, and the error is easy to miss unless logging is set to FINE.
Diagnosis: cf logs --recent shows [EL Warning] with Error Code: 267 and specified length too long for its datatype during startup. Or, if logging is not at FINE level, all JPA queries fail with Could not find table/view.
Solution: Replace length = N (where N > 5000) with columnDefinition = "NCLOB" on all @Lob fields. See Step 4 — HANA Cloud @Lob Column Migration above.
Cause: CXFNonSpringJaxrsServlet is declared in web.xml. This servlet instantiates JAX-RS endpoint classes directly via new, bypassing TomEE's CDI container entirely. All @Inject, @EJB, and @Resource fields remain null. The error manifests as NullPointerException when any injected service method is called (e.g., "Cannot invoke ... because this.myService is null").
Solution: See Step 7 above. Remove the CXFNonSpringJaxrsServlet <servlet> and <servlet-mapping> from web.xml. TomEE's built-in CXF integration auto-discovers @ApplicationPath and manages endpoint lifecycle through CDI. Move any jaxrs.providers init-param registrations to the Application.getClasses() method.
Cause: The most common cause is that the REST API URL patterns (e.g., /rest/*, /api/*) are not covered by a <security-constraint> in web.xml. The XSSecurityAuthenticator valve only validates JWT tokens for URLs that match a security constraint. Without a matching constraint, the valve skips JWT validation entirely — getUserPrincipal() returns null and all isUserInRole() calls return false, regardless of injection method (@Context, @Inject, or SessionContext). This is easy to miss when Neo-era constraints covered only static pages while REST APIs were reached via auth-method-specific URL prefixes (/s/api/*, /b/api/*) that were removed during migration.
Diagnosis: Check if getUserPrincipal() returns null. If it does, this is a missing security constraint issue, not an injection issue.
Solution: Add a <security-constraint> covering /rest/* (or /*) with <role-name>Everyone</role-name>. See the authentication-xsuaa skill, Step 5.
Cause: If getUserPrincipal() returns a valid principal but isUserInRole() still returns false: @Context HttpServletRequest is a JAX-RS annotation. It only works reliably in JAX-RS resource classes (endpoints). In @Stateless EJBs that are not JAX-RS resources — such as service providers, CDI producers, or utility beans — the @Context request is a CXF-internal wrapper that doesn't delegate isUserInRole() to the Catalina/XSUAA security realm. This causes isUserInRole("admin") to return false even though the user has the role assigned, resulting in authorization failures.
Important: SessionContext.isCallerInRole() also does not work in this scenario. TomEE's OpenEJBSecurityListener$RequestCapturer does not fully propagate XSUAA roles from the Catalina security realm to the OpenEJB security context, so sessionContext.isCallerInRole() returns false as well.
Solution: Replace @Context (JAX-RS injection) with @Inject (CDI injection) for HttpServletRequest. In Jakarta EE 10 (CDI 4.0), HttpServletRequest is a built-in CDI bean. CDI injects a request-scoped proxy that correctly delegates to the actual Catalina request — which has the XSUAA security context set by XSSecurityAuthenticator. Both isUserInRole() and getUserPrincipal() then work correctly.
Before (broken — @Context in non-JAX-RS EJB):
@Stateless
public class MyUserContext {
@Context // JAX-RS injection — gives CXF wrapper, no XSUAA context
HttpServletRequest httpRequest;
public boolean isAdmin() {
return httpRequest.isUserInRole("admin"); // returns false!
}
}
Also broken — SessionContext doesn't get XSUAA roles either:
@Stateless
public class MyUserContext {
@Resource
SessionContext sessionContext;
public boolean isAdmin() {
return sessionContext.isCallerInRole("admin"); // also returns false!
}
}
After (works — @Inject CDI injection):
@Stateless
public class MyUserContext {
@Inject // CDI injection — gives real Catalina request with XSUAA context
HttpServletRequest httpRequest;
public boolean isAdmin() {
return httpRequest.isUserInRole("admin"); // correct!
}
public String getEmail() {
Principal p = httpRequest.getUserPrincipal();
if (p instanceof Token) {
return ((Token) p).getClaimAsString(TokenClaims.EMAIL);
}
return null;
}
}
Note:
@Contextcontinues to work correctly in JAX-RS resource classes (@Path-annotated endpoints). This issue only affects@Stateless/@SingletonEJBs that are injected via CDI into JAX-RS endpoints.
Cause: beans.xml uses the old javax namespace (http://java.sun.com/xml/ns/javaee/beans_1_0.xsd). TomEE 10 may not process bean discovery correctly with the legacy namespace.
Solution: See Step 8 above. Update beans.xml to the Jakarta CDI 4.0 namespace (https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd) with bean-discovery-mode="all" and version="4.0".
| Feature | Tomcat | TomEE |
|---|---|---|
| EJB Support | No | Yes |
| JPA/JTA | Manual setup | Built-in |
| CDI | via extension | Built-in |
| Timer Service | No | Yes |
| Data Source Config | context.xml | resources.xml |
| Property | - | TARGET_RUNTIME: tomee |
| BOM | cf-tomcat-bom | cf-tomee-bom |
After completing TomEE migration: