Destinations
Configure the Destination service for outbound HTTP connections.
Purpose
Replace Neo's ConnectivityConfiguration and DestinationConfiguration JNDI lookups with SAP Cloud SDK's DestinationAccessor API for managing outbound connections.
Detection
This skill applies if any of these patterns are found:
In web.xml
<resource-ref>
<res-ref-name>connectivityConfiguration</res-ref-name>
<res-type>com.sap.core.connectivity.api.configuration.ConnectivityConfiguration</res-type>
</resource-ref>
In Java source files
import com.sap.core.connectivity.api.configuration.ConnectivityConfiguration;
import com.sap.core.connectivity.api.configuration.DestinationConfiguration;
// JNDI lookup pattern
Context ctx = new InitialContext();
ConnectivityConfiguration config =
(ConnectivityConfiguration) ctx.lookup("java:comp/env/connectivityConfiguration");
DestinationConfiguration destConfig = config.getConfiguration("my-destination");
Prerequisites
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:
- sdk-replacement -
Use the sdk-replacement skill- Sets up SAP Cloud SDK
- REQUIRED before this skill
Transformation Steps
Step 0: Detect existing JSON and HTTP conventions
Before generating any code, scan for existing library usage:
# JSON libraries
grep -E "fasterxml\.jackson|com\.google\.code\.gson|org\.json|jakarta\.json" pom.xml
grep -rl "ObjectMapper\|new Gson()\|new JSONObject\|Json\.create" --include="*.java" src/main/java/ 2>/dev/null | head -5
# HTTP client libraries
grep -E "httpclient|httpclient5|okhttp" pom.xml
Rule: Reuse what is found. Only introduce a new library if nothing is present. Default: SAP Cloud SDK HttpClientAccessor for HTTP (already present after sdk-replacement).
Step 1: Remove Resource References from web.xml
Remove these from web.xml:
<resource-ref>
<res-ref-name>connectivityConfiguration</res-ref-name>
<res-type>com.sap.core.connectivity.api.configuration.ConnectivityConfiguration</res-type>
</resource-ref>
Also remove AuthenticationHeaderProvider if present:
<resource-ref>
<res-ref-name>authenticationHeaderProvider</res-ref-name>
<res-type>com.sap.core.connectivity.api.authentication.AuthenticationHeaderProvider</res-type>
</resource-ref>
Note: The SAP Cloud SDK handles authentication automatically - no manual header management needed.
Step 2: Update Import Statements
Before:
import javax.naming.Context;
import javax.naming.InitialContext;
import com.sap.core.connectivity.api.configuration.ConnectivityConfiguration;
import com.sap.core.connectivity.api.configuration.DestinationConfiguration;
After:
import com.sap.cloud.sdk.cloudplatform.connectivity.DestinationAccessor;
import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination;
import com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationNotFoundException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.HttpResponse;
import static com.sap.cloud.sdk.cloudplatform.connectivity.HttpClientAccessor.getHttpClient;
Step 3: Replace Destination Lookup Code
Before (Neo):
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String destinationName = "my-destination";
try {
// JNDI lookup for connectivity configuration
Context ctx = new InitialContext();
ConnectivityConfiguration configuration =
(ConnectivityConfiguration) ctx.lookup("java:comp/env/connectivityConfiguration");
// Get destination configuration
DestinationConfiguration destConfiguration =
configuration.getConfiguration(destinationName);
if (destConfiguration == null) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Destination not found: " + destinationName);
return;
}
// Get URL from destination
String url = destConfiguration.getProperty("URL");
URL targetUrl = new URL(url);
// Get proxy settings
String proxyType = destConfiguration.getProperty("ProxyType");
Proxy proxy = getProxy(proxyType);
// Open connection with proxy
HttpURLConnection urlConnection = (HttpURLConnection) targetUrl.openConnection(proxy);
// Copy response
InputStream instream = urlConnection.getInputStream();
// ... handle response
} catch (NamingException e) {
throw new ServletException(e);
}
}
After (Cloud Foundry):
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String destinationName = "my-destination";
try {
// Get destination using SAP Cloud SDK
HttpDestination destination;
try {
destination = DestinationAccessor.getDestination(destinationName).asHttp();
} catch (DestinationNotFoundException e) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Destination not found: " + destinationName);
return;
}
// Get HTTP client configured for the destination
// Automatically handles proxy, authentication, etc.
HttpClient httpClient = getHttpClient(destination);
// Make request
HttpGet httpGet = new HttpGet(destination.getUri());
HttpResponse destinationResponse = httpClient.execute(httpGet);
// Check status
int statusCode = destinationResponse.getStatusLine().getStatusCode();
if (statusCode != HttpServletResponse.SC_OK) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Destination returned: " + statusCode);
return;
}
// Write response
destinationResponse.getEntity().writeTo(response.getOutputStream());
} catch (Exception e) {
throw new ServletException("Connectivity failed: " + e.getMessage(), e);
}
}
Step 4: Handle Destination Properties
If you need to access destination properties directly:
Before (Neo):
DestinationConfiguration destConfig = config.getConfiguration(destinationName);
String url = destConfig.getProperty("URL");
String user = destConfig.getProperty("User");
String proxyType = destConfig.getProperty("ProxyType");
After (Cloud Foundry):
import com.sap.cloud.sdk.cloudplatform.connectivity.Destination;
import com.sap.cloud.sdk.cloudplatform.connectivity.DestinationProperty;
Destination destination = DestinationAccessor.getDestination(destinationName);
// Get URI
URI uri = destination.asHttp().getUri();
// Get custom properties
Optional<String> customProperty = destination.get("customProperty");
// Check proxy type
ProxyType proxyType = destination.asHttp().getProxyType();
boolean isOnPremise = proxyType == ProxyType.ON_PREMISE;
Step 5: Add runtime dependency to pom.xml
The SAP Cloud SDK's DestinationAccessor requires connectivity-destination-service on the runtime classpath. Without it the SDK compiles fine but throws DestinationNotFoundException for every destination at runtime.
Check if it is already present:
grep -q "connectivity-destination-service" pom.xml && echo "already present" || echo "MISSING — will add"
If missing, add it to the <dependencies> section of pom.xml:
<!-- Required at runtime — DestinationAccessor fails without this -->
<dependency>
<groupId>com.sap.cloud.sdk.cloudplatform</groupId>
<artifactId>connectivity-destination-service</artifactId>
<scope>runtime</scope>
</dependency>
Add this inside the existing
<dependencies>block, alongside other SAP Cloud SDK dependencies added by thesdk-replacementskill. No version needed — it is managed bysdk-modules-bom.
Step 6: Update MTA Descriptor
Add destination service to mtad.yaml:
modules:
- name: ${app-name}
type: java.tomcat
path: target/<artifactId>.war
parameters:
buildpack: sap_java_buildpack_jakarta
disk-quota: 1024M
memory: 1024M
properties:
ENABLE_SECURITY_JAVA_API_V2: true
SET_LOGGING_LEVEL: 'ROOT: INFO'
requires:
- name: ${app-name}-destination
resources:
- name: ${app-name}-destination
type: org.cloudfoundry.managed-service
parameters:
service: destination
service-plan: lite
Step 7: Create Destinations in BTP Cockpit
Create destinations via BTP Cockpit or programmatically:
Option A: BTP Cockpit
- Navigate to your subaccount
- Go to Connectivity → Destinations
- Click "New Destination"
- Configure:
- Name:
my-destination - Type: HTTP
- URL:
https://api.example.com - Proxy Type: Internet (or OnPremise)
- Authentication: NoAuthentication / BasicAuthentication / OAuth2...
- Name:
Option B: Destination Service REST API
# Get access token
TOKEN=$(cf oauth-token | sed 's/bearer //')
# Create destination
curl -X POST \
"https://destination-configuration.cfapps.${region}.hana.ondemand.com/destination-configuration/v1/subaccountDestinations" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"Name": "my-destination",
"Type": "HTTP",
"URL": "https://api.example.com",
"ProxyType": "Internet",
"Authentication": "NoAuthentication"
}'
Configuration Files
No new configuration files in the application. Destinations are managed in BTP Cockpit.
Required Runtime Dependency
The SAP Cloud SDK's DestinationAccessor requires connectivity-destination-service on the runtime classpath. Add it to pom.xml:
<!-- Required at runtime — DestinationAccessor fails with DESTINATION_NOT_FOUND without this -->
<dependency>
<groupId>com.sap.cloud.sdk.cloudplatform</groupId>
<artifactId>connectivity-destination-service</artifactId>
<scope>runtime</scope>
</dependency>
Why
runtimescope? The artifact contains the CF-specificDestinationServiceimplementation that the SDK loads via ServiceLoader at runtime. The compilation succeeds without it (the API is inscp-cf), butDestinationAccessor.getDestination()silently falls back to a no-op loader and throwsDestinationNotFoundExceptionfor every call.
CF Services
| Service | Plan | Purpose |
|---|---|---|
destination |
lite | Destination configuration service |
Verification
1. Compile Check
mvn clean compile
2. Test Destination Access
Add a test endpoint:
@WebServlet("/test/destination")
public class DestinationTestServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String destName = req.getParameter("name");
try {
HttpDestination dest = DestinationAccessor.getDestination(destName).asHttp();
resp.getWriter().println("Destination found: " + dest.getUri());
} catch (DestinationNotFoundException e) {
resp.sendError(404, "Destination not found: " + destName);
}
}
}
3. Deploy and Test
mvn clean package
cf deploy . -f
# Test
curl "https://${app-url}/test/destination?name=my-destination"
Common Issues
Issue: DestinationNotFoundException — destination exists in BTP Cockpit but SDK throws anyway
Root cause: Missing connectivity-destination-service runtime dependency in pom.xml. The SDK compiles without it (the API is in scp-cf), but at runtime the CF-specific destination loader is not on the classpath. DestinationAccessor falls back to a no-op loader and throws DestinationNotFoundException for every destination — even ones that exist.
Fix: Add to pom.xml:
<dependency>
<groupId>com.sap.cloud.sdk.cloudplatform</groupId>
<artifactId>connectivity-destination-service</artifactId>
<scope>runtime</scope>
</dependency>
Verify after redeploy:
# Should show the destination properties, not an error
cf ssh <app-name> -c 'env | grep VCAP_SERVICES' | python3 -m json.tool | grep -A5 destination
Issue: DestinationNotFoundException — name mismatch
Cause: Destination name in code doesn't match exactly what is configured in BTP Cockpit (case-sensitive). Fix: Verify destination name in BTP Cockpit → Connectivity → Destinations. Names are case-sensitive.
Issue: 403 Forbidden when accessing destination
Cause: Authentication configuration issue in the destination definition. Fix: Check destination authentication settings and credentials in BTP Cockpit.
Issue: Connection timeout
Cause: Network or firewall issue. Fix:
- For Internet destinations: check URL is reachable from CF
- For OnPremise: check Cloud Connector configuration (see
connectivity-onpremiseskill)
API Reference
DestinationAccessor
// Get destination by name
Destination dest = DestinationAccessor.getDestination("name");
// Get as HTTP destination
HttpDestination httpDest = dest.asHttp();
// Try to get destination (returns Optional)
Option<Destination> maybeDest = DestinationAccessor.tryGetDestination("name");
HttpDestination
// Get URI
URI uri = httpDest.getUri();
// Get proxy type
ProxyType proxyType = httpDest.getProxyType();
// Get headers
Collection<Header> headers = httpDest.getHeaders();
HttpClientAccessor
// Get HTTP client for destination
HttpClient client = HttpClientAccessor.getHttpClient(httpDest);
// Execute request
HttpResponse response = client.execute(new HttpGet(httpDest.getUri()));
Next Steps
After completing this skill, proceed to:
- ../connectivity-onpremise/SKILL.md - For on-premise connectivity
- ../mail-destinations/SKILL.md - For mail configuration via destinations