Reputation: 63
I created an APM Business Application in SAP Web IDE Full-Stack.
The S/4HANA SDK reads business partners with the S/4HANA API. Custom business partner data is persisted in a SAP Cloud Platform HDI container. The business partner entity has an 0..1 association with the custom entity.
The OData navigation property is successful when the custom data exists but returns an Null Pointer Exception when it does not exist. The OData $expand is not successful in either case, returning error while trying to invoke the method java.util.Map.size() of a null object loaded from local variable 'm'
. I may be incorrectly expecting the associated entity to return the key property’s value but not the values of the other properties.
Below are the data model, service, business partner operations, error, and logs.
Data model CDS
entity BusinessPartner {
Key BusinessPartner : String(10);
LastName : String(40);
FirstName : String(40);
status : Association to PartnerStatus;
}
entity PartnerStatus {
Key BusinessPartner : String(10);
StatusConfirmed : String(1);
}
Service CDS
service BusinessPartnerService {
@cds.persistence.skip
entity BusinessPartner @readonly as projection on s4c.BusinessPartner;
entity PartnerStatus @readonly as projection on s4c.PartnerStatus;
}
Business Partner @Query and @Read
@Query(serviceName = "BusinessPartnerService", entity = "BusinessPartner")
public QueryResponse queryPartners(QueryRequest queryRequest) throws ServletException {
final List<BusinessPartner> businessPartners;
try {
businessPartners = new DefaultBusinessPartnerService()
.getAllBusinessPartner()
.top(5)
.execute();
} catch (ODataException e) {
throw new ServletException(e);
}
QueryResponse queryResponse = QueryResponse.setSuccess().setData(businessPartners).response();
return queryResponse;
}
@Read(serviceName = "BusinessPartnerService", entity = "BusinessPartner")
public ReadResponse readPartner(ReadRequest readRequest) throws ServletException {
String id = String.valueOf(readRequest.getKeys().get("BusinessPartner"));
final BusinessPartner businessPartner;
try {
businessPartner = new DefaultBusinessPartnerService()
.getBusinessPartnerByKey(id)
.execute();
} catch (ODataException e) {
throw new ServletException(e);
}
ReadResponse readResponse = ReadResponse.setSuccess().setData(businessPartner).response();
return readResponse;
}
Error
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<code>CDSRuntimeException.NO_ENTITY_FOUND</code>
<message xml:lang="en-US">No Entity found for the URL.</message>
</error>
Logs
2018-11-06T11:02:50.53-0500 [APP/PROC/WEB/0] OUT { "written_at":"2018-11-06T16:02:50.538Z","written_ts":1056610886992277,"component_id":"df464a53-1905-4c4b-bcc5-ef59761d2218","component_name":"ge2qqww1rr7SxSEK-apm-s4c-srv","DCComponent":"","organization_name":"-","component_type":"application","space_name":"dev","component_instance":"0","organization_id":"-","correlation_id":"-","CSNComponent":"","space_id":"5e8e1eb0-f9ca-40d8-8ba9-1880ca345628","Application":"ge2qqww1rr7SxSEK-apm-s4c-srv","container_id":"10.0.137.3","type":"log","logger":"com.sap.cloud.sdk.service.prov.v2.rt.core.CloudSDKODataErrorCallback","thread":"http-nio-0.0.0.0-3000-exec-2","level":"ERROR","categories":[],"msg":"Request URL: https://ge2qqww1rr7sxsek-apm-s4c-srv.cfapps.us10.hana.ondemand.com/odata/v2/BusinessPartnerService/BusinessPartner?$expand=status\nStatusCode:500","stacktrace":["java.lang.NullPointerException: while trying to invoke the method java.util.Map.size() of a null object loaded from local variable 'm'","\tat java.util.HashMap.putMapEntries(HashMap.java:501)","\tat java.util.HashMap.putAll(HashMap.java:785)","\tat com.sap.gateway.core.api.provider.data.BaseDataProvider.getSingleNavigationPathResult(BaseDataProvider.java:837)","\tat com.sap.gateway.core.api.provider.data.BaseDataProvider.readExpandedEntitySet(BaseDataProvider.java:915)","\tat com.sap.cloud.sdk.service.prov.v2.data.provider.CXSDataProvider.readExpandedEntitySet(CXSDataProvider.java:724)","\tat com.sap.cloud.sdk.service.prov.v2.rt.data.provider.HybridDataProvider.readExpandedEntitySet(HybridDataProvider.java:298)","\tat com.sap.gateway.core.api.provider.data.GenericODataProcessor.readEntitySet(GenericODataProcessor.java:895)","\tat org.apache.olingo.odata2.core.Dispatcher.dispatch(Dispatcher.java:77)","\tat org.apache.olingo.odata2.core.ODataRequestHandler.handle(ODataRequestHandler.java:131)","\tat org.apache.olingo.odata2.core.servlet.ODataServlet.handleRequest(ODataServlet.java:216)","\tat org.apache.olingo.odata2.core.servlet.ODataServlet.handle(ODataServlet.java:115)","\tat org.apache.olingo.odata2.core.servlet.ODataServlet.service(ODataServlet.java:85)","\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:742)","\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)","\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)","\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)","\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)","\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)","\tat com.sap.cloud.sdk.cloudplatform.servlet.RequestContextServletFilter.lambda$doFilter$0(RequestContextServletFilter.java:171)","\tat com.sap.cloud.sdk.cloudplatform.servlet.RequestContextCallable.call(RequestContextCallable.java:95)","\tat com.sap.cloud.sdk.cloudplatform.servlet.RequestContextServletFilter.doFilter(RequestContextServletFilter.java:173)","\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)","\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)","\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)","\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)","\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493)","\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)","\tat com.sap.xs.java.valves.ErrorReportValve.invoke(ErrorReportValve.java:66)","\tat ch.qos.logback.access.tomcat.LogbackValve.invoke(LogbackValve.java:191)","\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)","\tat com.sap.xs.jdbc.datasource.valve.JDBCValve.invoke(JDBCValve.java:62)","\tat com.sap.xs.security.UserInfoValve.invoke(UserInfoValve.java:19)","\tat com.sap.xs.statistics.tomcat.valve.RequestTracingValve.invoke(RequestTracingValve.java:43)","\tat com.sap.xs.logging.catalina.RuntimeInfoValve.invoke(RuntimeInfoValve.java:40)","\tat org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:685)","\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)","\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800)","\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)","\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:800)","\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471)","\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)","\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)","\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)","\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)","\tat java.lang.Thread.run(Thread.java:836)"] }
Upvotes: 4
Views: 1678
Reputation: 63
I used CDSDataSourceHandler to read the local entity.
If the local entity exists, it is returned. If the local entity does not exist, the keys are passed to the entity from the source entity.
private static Connection getConnection() {
Connection conn = null;
Context ctx;
try {
ctx = new InitialContext();
conn = ((DataSource) ctx.lookup("java:comp/env/jdbc/java-hdi-container")).getConnection();
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
@Read(serviceName = "BusinessPartnerService", entity = "PartnerStatus", sourceEntity = "BusinessPartner")
public ReadResponse readPartnerStatus(ReadRequest readRequest) {
CDSDataSourceHandler dsHandler = DataSourceHandlerFactory.getInstance().getCDSHandler(getConnection(), readRequest.getEntityMetadata().getNamespace());
EntityData ed = null;
try {
ed = dsHandler.executeRead(readRequest.getEntityMetadata().getName(), readRequest.getKeys(), readRequest.getEntityMetadata().getElementNames());
} catch (CDSException e) { }
if (ed != null) {
return ReadResponse.setSuccess().setData(ed).response();
} else {
Map<String, Object> keys = readRequest.getKeys();
Object keyObject = keys.get("BusinessPartner");
String id = (String)keyObject;
return ReadResponse.setSuccess().setData(ImmutableMap.of("BusinessPartner", id, "StatusConfirmed", "X")).response();
}
}
The OData $expand is then successful for both the entity and entity set.
Upvotes: 2
Reputation: 567
The join across the remote entity and the local entity does not happen automatically. You have to implement this manually using a Read operation for the associated entity PartnerStatus that also specifies the source entity for the navigation BusinessPartner. This method would look as follows (as you can see, in this example I am always returning dummy data):
@Read(serviceName = "BusinessPartnerService",
entity = "PartnerStatus",
sourceEntity = "BusinessPartner")
public ReadResponse readPartnerStatus(ReadRequest readRequest) throws ServletException {
return ReadResponse.setSuccess().setData(
ImmutableMap.of("BusinessPartner", "1003764", "StatusConfirmed", "X"))
.response();
}
The documentation about the Read operation contains more details on this concept.
You would now need to access the database, for example, using JPA (I have not tried this out) in the handler. The documentation has a section on Using JPA in Custom Handlers.
Upvotes: 1