Configures HANA Cloud database connectivity for Java apps on Cloud Foundry by replacing Neo JNDI DataSource references in web.xml or code with schema bindings.
npx claudepluginhub sap-samples/btp-neo-java-app-migration --plugin sap-btp-neo-migrationThis skill is limited to using the following tools:
Configure HANA Cloud database connectivity for 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 HANA Cloud database connectivity for Cloud Foundry.
Replace Neo's javax.sql.DataSource JNDI resource reference with Cloud Foundry's HANA schema binding using the SAP Java Buildpack's TomcatDataSourceFactory.
This skill applies if any of these patterns are found:
<resource-ref>
<res-ref-name>jdbc/DefaultDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
</resource-ref>
import javax.sql.DataSource;
import javax.annotation.Resource;
@Resource(name = "jdbc/DefaultDB")
private DataSource dataSource;
// OR JNDI lookup
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/DefaultDB");
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 sdk-replacement skill
Also required:
Remove this from web.xml:
<resource-ref>
<res-ref-name>jdbc/DefaultDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
</resource-ref>
Create src/main/webapp/META-INF/context.xml - see assets/context.xml:
<?xml version='1.0' encoding='utf-8'?>
<Context>
<Resource name="jdbc/DefaultDB"
auth="Container"
type="javax.sql.DataSource"
factory="com.sap.xs.jdbc.datasource.tomcat.TomcatDataSourceFactory"
service="${service_name_for_DefaultDB}"/>
</Context>
Note: The
${service_name_for_DefaultDB}placeholder is replaced at runtime via theJBP_CONFIG_RESOURCE_CONFIGURATIONenvironment variable.
Create src/main/webapp/META-INF/sap_java_buildpack/config/resource_configuration.yml - see assets/resource_configuration.yml:
---
tomcat/webapps/ROOT/META-INF/context.xml:
service_name_for_DefaultDB: ${app-name}-hana
Note: Replace
${app-name}-hanawith your actual HANA service instance name defined in mtad.yaml.
Add HANA schema resource and configuration to mtad.yaml:
_schema-version: "3.2"
ID: ${app-name}
version: 0.0.1
modules:
- name: ${app-name}
type: java.tomcat
path: target/${app-name}.war
parameters:
buildpack: sap_java_buildpack_jakarta
disk-quota: 512MB
memory: 512MB
properties:
ENABLE_SECURITY_JAVA_API_V2: true
SET_LOGGING_LEVEL: 'ROOT: INFO'
# Resource configuration - MUST use YAML list format
JBP_CONFIG_RESOURCE_CONFIGURATION:
- tomcat/webapps/ROOT/META-INF/context.xml:
service_name_for_DefaultDB: ${app-name}-hana
requires:
- name: ${app-name}-hana
resources:
- name: ${app-name}-hana
type: com.sap.xs.hana-schema
The Java code using DataSource via JNDI lookup continues to work:
import javax.sql.DataSource;
import javax.naming.Context;
import javax.naming.InitialContext;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class PersistenceServlet extends HttpServlet {
private DataSource getDataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/DefaultDB");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try (Connection conn = getDataSource().getConnection()) {
// Create table if not exists
String createTable = "CREATE TABLE IF NOT EXISTS PERSONS " +
"(ID INTEGER PRIMARY KEY, NAME VARCHAR(255))";
try (PreparedStatement stmt = conn.prepareStatement(createTable)) {
stmt.execute();
}
// Query data
String query = "SELECT * FROM PERSONS";
try (PreparedStatement stmt = conn.prepareStatement(query);
ResultSet rs = stmt.executeQuery()) {
response.setContentType("application/json");
response.getWriter().println("[");
boolean first = true;
while (rs.next()) {
if (!first) response.getWriter().println(",");
response.getWriter().printf("{\"id\": %d, \"name\": \"%s\"}",
rs.getInt("ID"), rs.getString("NAME"));
first = false;
}
response.getWriter().println("]");
}
} catch (Exception e) {
throw new ServletException("Database error: " + e.getMessage(), e);
}
}
}
If using @Resource annotation, it continues to work:
import javax.annotation.Resource;
import javax.sql.DataSource;
public class PersistenceServlet extends HttpServlet {
@Resource(name = "jdbc/DefaultDB")
private DataSource dataSource;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try (Connection conn = dataSource.getConnection()) {
// ... use connection
} catch (SQLException e) {
throw new ServletException(e);
}
}
}
After applying this skill, your project should have:
src/main/webapp/
├── META-INF/
│ ├── context.xml # DataSource configuration
│ └── sap_java_buildpack/
│ └── config/
│ └── resource_configuration.yml # Service name mapping
└── WEB-INF/
└── web.xml # (resource-ref removed)
| File | Location | Purpose |
|---|---|---|
context.xml | src/main/webapp/META-INF/ | Defines JNDI DataSource |
resource_configuration.yml | src/main/webapp/META-INF/sap_java_buildpack/config/ | Maps service name placeholder |
| Service | Type | Purpose |
|---|---|---|
${app-name}-hana | com.sap.xs.hana-schema | HANA Cloud schema binding |
If you don't have a HANA Cloud instance:
# List available plans
cf marketplace -s hana
# Create HANA schema
cf create-service hana schema ${app-name}-hana
ls -la src/main/webapp/META-INF/
# Should show: context.xml
ls -la src/main/webapp/META-INF/sap_java_buildpack/config/
# Should show: resource_configuration.yml
mvn clean package
cf deploy . -f
cf services
# Should show ${app-name}-hana bound to ${app-name}
cf env ${app-name} | grep -A 20 "hana"
# Should show HANA credentials
curl "https://${app-url}/persistence"
# Should return database query results
cf logs ${app-name} --recent | grep -i "datasource\|hana\|jdbc"
Cause: HANA JDBC driver not available.
Solution: The SAP Java Buildpack includes HANA drivers. Ensure you're using sap_java_buildpack_jakarta.
Cause: HANA schema service doesn't exist. Solution:
cf create-service hana schema ${app-name}-hana
Cause: HANA Cloud instance not running or IP allowlist. Solution:
Cause: Schema not set correctly or table doesn't exist. Solution: HANA schema services automatically set the current schema. Verify table names are correct.
Cause: YAML syntax error or wrong path. Solution:
tomcat/webapps/ROOT/META-INF/context.xmlIf your application uses JPA, update persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<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="default" transaction-type="RESOURCE_LOCAL">
<non-jta-data-source>java:comp/env/jdbc/DefaultDB</non-jta-data-source>
<properties>
<property name="jakarta.persistence.schema-generation.database.action"
value="create"/>
<property name="eclipselink.logging.level" value="INFO"/>
</properties>
</persistence-unit>
</persistence>
After completing this skill, proceed to other applicable skills: