Skip to Content
Author's profile photo Santosh Kumar

How to create Olingo V2.0 OData service with Spring Cloud in CloudFoundry ?

In this blog, we will see an example of Olingo OData v2.0 service that consumes HANA DB service on Cloud foundry. This example makes use of Spring cloud connectors together with Spring Cloud HANA Service Connector for connecting to HANA Service in Cloud foundry.

We are going to require below tools/frameworks :

  • Eclipse with JDK 1.8 for development.
  • Spring Boot 1.5.2 for Spring web application.
  • Spring Cloud Connector 1.2.3.RELEASE for connecting to cloud foundry services.
  • Olingo OData V2.0.8-sap-05 for OData service.
  • eclipse link 2.6.4 for JPA.
  • HANA service connector 1.0.4.RELEASE for connecting to HANA service.
  • Maven 3.0.5 or higher for application build.
  • CloudFoundry CommandLineInterface(CLI) for application deployment to Cloud foundry.

Lets start with pom.xml defining all the required dependencies. Complete code can be downloaded from here

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>ODataSpring</groupId>
	<artifactId>ODataSpring</artifactId>
	<version>0.1</version>
	<packaging>war</packaging>
	<properties>
		<eclipselink.version>2.6.4</eclipselink.version>
		<spring.cloud>1.2.3.RELEASE</spring.cloud>
		<!-- Using SAP internal verison for Olingo. This could be replaced with 
			Olingo global version . -->
		<olingo.v2.version>2.0.8-sap-05</olingo.v2.version>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
		<maven.compiler.showDeprecation>true</maven.compiler.showDeprecation>
	</properties>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.2.RELEASE</version>
	</parent>
	<dependencies>
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-frontend-jaxrs</artifactId>
			<version>3.2.0</version>
			<exclusions>
				<exclusion>
					<artifactId>geronimo-javamail_1.4_spec</artifactId>
					<groupId>org.apache.geronimo.specs</groupId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.apache.olingo</groupId>
			<artifactId>olingo-odata2-jpa-processor-api</artifactId>
			<version>${olingo.v2.version}</version>
			<exclusions>
				<exclusion>
					<groupId>org.eclipse.persistence</groupId>
					<artifactId>javax.persistence</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.apache.olingo</groupId>
			<artifactId>olingo-odata2-jpa-processor-core</artifactId>
			<version>${olingo.v2.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.olingo</groupId>
			<artifactId>olingo-odata2-jpa-processor-ref</artifactId>
			<version>${olingo.v2.version}</version>
			<scope>provided</scope>
			<exclusions>
				<exclusion>
					<artifactId>slf4j-api</artifactId>
					<groupId>org.slf4j</groupId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.apache.olingo</groupId>
			<artifactId>olingo-odata2-core</artifactId>
			<version>${olingo.v2.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.olingo</groupId>
			<artifactId>olingo-odata2-api-annotation</artifactId>
			<version>${olingo.v2.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.olingo</groupId>
			<artifactId>
   			olingo-odata2-annotation-processor-ref
   		</artifactId>
			<version>${olingo.v2.version}</version>
			<scope>provided</scope>
			<exclusions>
				<exclusion>
					<artifactId>slf4j-log4j12</artifactId>
					<groupId>org.slf4j</groupId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.apache.olingo</groupId>
			<artifactId>
   			olingo-odata2-annotation-processor-core
   		</artifactId>
			<version>${olingo.v2.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<exclusions>
			</exclusions>
		</dependency>

		<!-- Spring JPA. Since, we are using eclipse link JPA. We exclude default 
			hibernate JPA. -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.hibernate</groupId>
					<artifactId>hibernate-entitymanager</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.eclipse.persistence</groupId>
			<artifactId>eclipselink</artifactId>
			<version>${eclipselink.version}</version>
		</dependency>
		<dependency>
			<groupId>com.jayway.jsonpath</groupId>
			<artifactId>json-path</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<artifactId>slf4j-api</artifactId>
					<groupId>org.slf4j</groupId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>joda-time</groupId>
			<artifactId>joda-time</artifactId>
			<!-- <version>2.9.9</version> -->
		</dependency>

		<!-- spring cloud service connector for CF -->
		<!-- For deployment in cloud foundry -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-spring-service-connector</artifactId>
			<!-- <version>${spring.cloud}</version> -->
		</dependency>
		<!-- Cloud foundry connector -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-cloudfoundry-connector</artifactId>
			<!-- <version>${spring.cloud}</version> -->
		</dependency>
		<!-- HANA service connector for connecting to HANA Service in SAP CF. -->
		<dependency>
			<groupId>com.sap.hana.cloud</groupId>
			<artifactId>spring-cloud-cloudfoundry-hana-service-connector</artifactId>
			<version>1.0.4.RELEASE</version>
		</dependency>
	</dependencies>
	<build>
		<sourceDirectory>src</sourceDirectory>
		<resources>
			<resource>
				<directory>src</directory>
				<excludes>
					<exclude>**/*.java</exclude>
				</excludes>
			</resource>
		</resources>
		<plugins>
			<plugin>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.6</version>
				<configuration>
					<warSourceDirectory>WebContent</warSourceDirectory>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
			<plugin>
				<groupId>de.empulse.eclipselink</groupId>
				<artifactId>staticweave-maven-plugin</artifactId>
				<version>1.0.0</version>
				<executions>
					<execution>
						<phase>process-classes</phase>
						<goals>
							<goal>weave</goal>
						</goals>
						<configuration>
							<persistenceXMLLocation>META-INF/persistence.xml</persistenceXMLLocation>
							<logLevel>FINE</logLevel>
						</configuration>
					</execution>
				</executions>
				<dependencies>
					<dependency>
						<groupId>org.eclipse.persistence</groupId>
						<artifactId>org.eclipse.persistence.jpa</artifactId>
						<version>2.6.0</version>
					</dependency>
				</dependencies>
			</plugin>
		</plugins>
		<pluginManagement>
			<plugins>
				<!--This plugin's configuration is used to store Eclipse m2e settings 
					only. It has no influence on the Maven build itself. -->
				<plugin>
					<groupId>org.eclipse.m2e</groupId>
					<artifactId>lifecycle-mapping</artifactId>
					<version>1.0.0</version>
					<configuration>
						<lifecycleMappingMetadata>
							<pluginExecutions>
								<pluginExecution>
									<pluginExecutionFilter>
										<groupId>
											de.empulse.eclipselink
										</groupId>
										<artifactId>
											staticweave-maven-plugin
										</artifactId>
										<versionRange>
											[1.0.0,)
										</versionRange>
										<goals>
											<goal>weave</goal>
										</goals>
									</pluginExecutionFilter>
									<action>
										<ignore></ignore>
									</action>
								</pluginExecution>
							</pluginExecutions>
						</lifecycleMappingMetadata>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>
</project>

The dependencies in pom are:

  • Olingo OData SAP internal version 2.0.8-sap-05. Non SAP version should also be fine.
  • Spring boot 1.5.2 for Web application development. It takes care of adding the Spring core libraries.
  • Eclipse link persistence V2.6.4 for JPA.
  • Spring cloud service and Spring cloud foundry connectors which provide the required APIs for accessing CloudFoundry services in our code.
  • Spring cloud foundry HANA service connector for connecting to HANA service in SAP CF. This is mandatory since, Cloud foundry by default doesn’t support APIs for HANA service.

Notice that we also have enabled static weaving to prevent any LoadTime weaving exception when the application is booted.

Before proceeding further it is recommended to run ‘mvn package’ to make sure all the required libraries are downloaded and added to the class path. This will package our application into .war file.

The first Java class we create will be a simple JPA entity that maps to DB table Employee:

@Entity
public class Employee implements Serializable {

	@Id
	private String id;
	private String name;
	private String description;
	private static final long serialVersionUID = 1L;

	public Employee() {
		super();
	}

	public String getId() {
		return this.id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDescription() {
		return this.description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + ", description=" + description + "]";
	}

}

Next we will see persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
	xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
	<persistence-unit name="ODataSpring">
		<class>com.sap.cf.odata.spring.model.Employee</class>
		<properties>
			<property name="eclipselink.ddl-generation" value="create-tables" />
			<!-- Disable weaving to prevent load time weaver exception. Instead use 
				static weaving as mentioned in pom.xml -->
			<property name="eclipselink.weaving" value="false" />
		</properties>
	</persistence-unit>
</persistence>

In this file, we disabled weaving, since we already have enabled static weaving in pom.xml and this further prevents any load time weaving by Spring framework.

Now we create required Java classes. The package names and the imports are omitted for the sake of simplicity.

/**
 * Main class for this application. extends {@link SpringBootServletInitializer}
 * it making a web application for war deployment.
 * 
 * @author i324363
 *
 */

@SpringBootApplication
public class AppController extends SpringBootServletInitializer {

	// Main method gives control to Spring by invoking run on Spring Application.
	// This enables Spring to Bootstrap the application
	public static void main(String[] args) {
		SpringApplication.run(AppController.class, args);

	}

	// This method adds Configuration class for Spring Application Context builder
	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
		return builder.sources(AppController.class, CloudConfig.class);
	}
}

The main method delegates the application to SpringApplication by invoking the run method. The SpringApplication will then bootstrap our application.

Inside configure method we inform Spring Boot about our configuration class “CloudConfig” by invoking SpringApplicationBuilder’s sources method.

Next we create Configuration class that defines the beans required by our application.

@Configuration
@Profile("cloud")
@ComponentScan(basePackages = "com.sap.cf")
public class CloudConfig extends AbstractCloudConfig {

	private static final String HANA_SVC = "hana-schema-svc";

	private static final Logger LOG = LoggerFactory.getLogger(CloudConfig.class);

	/**
	 * Create dataSource bean from SAP CF
	 * 
	 * @return dataSource dataSoruce created from HANA Service.
	 */
	@Bean
	public DataSource dataSource() {
		DataSource dataSource = null;
		try {
			dataSource = connectionFactory().dataSource(HANA_SVC);
		} catch (CloudException ex) {
			LOG.error(" ", ex);
		}
		return dataSource;
	}

	/**
	 * Create Eclipselink EMF from the dataSource bean. JPAvendor and datasource
	 * will be set here. rest will be taken from persistence.xml
	 * 
	 * @return EntityManagerFactory
	 */
	@Bean
	public EntityManagerFactory entityManagerFactory() {
		LocalContainerEntityManagerFactoryBean springEMF = new LocalContainerEntityManagerFactoryBean();
		springEMF.setJpaVendorAdapter(new EclipseLinkJpaVendorAdapter());
		springEMF.setDataSource(dataSource());
		springEMF.afterPropertiesSet();
		return springEMF.getObject();

	}

	/**
	 * Registers OData servlet bean with Spring Application context to handle
	 * ODataRequests.
	 * 
	 * @return
	 */
	@Bean
	public ServletRegistrationBean odataServlet() {

		ServletRegistrationBean odataServRegstration = new ServletRegistrationBean(new CXFNonSpringJaxrsServlet(),
				"/odata.svc/*");
		Map<String, String> initParameters = new HashMap<>();
		initParameters.put("javax.ws.rs.Application", "org.apache.olingo.odata2.core.rest.app.ODataApplication");
		initParameters.put("org.apache.olingo.odata2.service.factory",
				"com.sap.cf.odata.spring.context.JPAServiceFactory");
		odataServRegstration.setInitParameters(initParameters);

		return odataServRegstration;

	}

}

In this class, we first create bean of following types in the application context :

  • datasource – is created from the HANA service created in cloud foundry. For this we use the  methods provided by the super class – AbstractCloudConfig. We could have omitted passing the name of the service to dataSource(), since it is the only data source service our application is bound to.
  • entityManagerFactory – we use the datasouce bean to create EMF instance. Notice that we use eclipse link for jpa instead of Spring default hibernate for the EMF.
  • odataServlet – Servlet required for OData is created with the required init parameters. This is analogous to creating the servlet in the web application deployment descriptor – web.xml using servlet XML tags.

Next we create the JPAServiceFactory that extends ODataJPAServiceFactory.

public class JPAServiceFactory extends ODataJPAServiceFactory {

	private static final String PERSISTENT_UNIT = "ODataSpring";
	private static final String EMF = "entityManagerFactory";

	private static final Logger LOG = LoggerFactory.getLogger(JPAServiceFactory.class);

	@Override
	public ODataJPAContext initializeODataJPAContext() throws ODataJPARuntimeException {
		ODataJPAContext oDataJPACtx = getODataJPAContext();
		EntityManagerFactory emf = (EntityManagerFactory) SpringContextUtil.getBean(EMF);
		LOG.debug("EMF in JPAservicefactory " + emf);
		oDataJPACtx.setEntityManagerFactory(emf);
		oDataJPACtx.setPersistenceUnitName(PERSISTENT_UNIT);
		oDataJPACtx.setDefaultNaming(true);
		this.setDetailErrors(true);
		return oDataJPACtx;
	}

}

Since, JPAServiceFactory is not managed by Spring we cannot auto wire emf bean into JPAServiceFactory. In order to  fetch emf we use a custom class SpringContextUtil. We will see that next.

@Component
public class SpringContextUtil implements ApplicationContextAware {

	private static ApplicationContext applicationContext;

	public SpringContextUtil() {

	}

	public static Object getBean(String beanName) throws BeansException {
		return applicationContext.getBean(beanName);
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		SpringContextUtil.applicationContext = applicationContext;
	}

}

Few points about this class :

  • extends ApplicationContextAware of Spring, so that it is notified of the SpringApplicationContext (where the required beans are stored). This is the simplest way to expose Spring application context beans to classes that are not Spring managed.
  • Provides a static getBean method to fetch beans from the Spring Application Context. This method should only be used by classes where bean injection is not possible.

Important: We also need to copy njdbc.jar  into WEB-INF/lib folder for the required HANA JDBC Driver class. As of now there is no maven dependency to add njdbc.jar.

Now lets see manifest.yml file for the deployment instructions to CLI:

---
applications:
- name: odatav2-spring
  path: target/ODataSpring-0.1.war
  services:
   - hana-schema-svc
  env:
   SPRING_PROFILES_DEFAULT: cloud

This file should be placed in the root directory of our application. It defines path to our application war, the HANA Service instance and the default profile as ‘cloud’.

Now lets build the application and before we run “cf push” from CLI, we should also have created a HANA Schema Service with the name ‘hana-schema-svc’ in the Cloud foundry service market place. Make sure to select ‘schema’ from the service plan below:

Finally execute cf push from the directory which has manifest.yml and hopefully if everything is fine the application should be deployed and the OData url should be accessible.

This concludes our sample CloudFoundry OData Service based on Spring 🙂

 

Further references and useful links :-

 

 

Assigned Tags

      10 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Ivan Mirisola
      Ivan Mirisola

      Hi Santosh,

      Great blog! Really appreciate it.

      Just one correction. Since this is a public blog, most users will not have access to the olingo libraries from our Nexus repo. I have tested it using a local project with the following dependency changes on my pom.xml file.

      <properties>
      ...
      <olingo.v2.version>2.0.10</olingo.v2.version>
      ...
      </properties>

      Regards,
      Ivan

      Author's profile photo Santosh Kumar
      Santosh Kumar
      Blog Post Author

      Hi Ivan,

      Thank you for the feedback and for the comment about adding the global olingo dependency instead of SAP Olingo.

       

      Regards,

      Santosh

      Author's profile photo Ivan Mirisola
      Ivan Mirisola

      Hi Santosh,

      How would I go about it If I were to create my own repository service to implement a custom service method? Let's say I wanted consume a Calculation View with input parameters in Java...

      Regards,
      Ivan

      Author's profile photo Saji Surendran
      Saji Surendran

      Hello Ivan,

      Did you get the answer for your above question? Do you have any link for the solution?

      when I hit the URL, I'll get a list of java entity class name in xml like below

      <service
      xml:base="URL:port-no/odata.svc/">
      <workspace>
      <atom:title>Default</atom:title>
      <collection href="CustomerMasters">
      <atom:title>CustomerMasters</atom:title>
      </collection>
      </workspace>
      </service>

      and if I use that entityset name at the end of URL like below

      URL:443/odata.svc/CustomerMasters then I'll ge the below output. I am not getting the real data.

      <feed xmlns="http://www.w3.org/2005/Atom" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xml:base="URL:443/odata.svc/">
      <id>
      URL:443/odata.svc/CustomerMasters
      </id>
      <title type="text">CustomerMasters</title>
      <updated>2018-10-22T06:29:12.192Z</updated>
      <author>
      <name/>
      </author>
      <link href="CustomerMasters" rel="self" title="CustomerMasters"/>
      </feed>

      Don't know where I am going wrong. how can I share code with you to check?

       

      Thanks

      Saji S.

      Author's profile photo Ivan Mirisola
      Ivan Mirisola

      Hi Saji,

      I didn't get an answer so far. However, the way I understood the blog is that it contains some basic odata automatic conversion of JPA Entities into Odata Collections (JPAServiceFactory).

      On the other hand, you no longer need this type of approach. You could use the new Cloud Application Programming Model. This is basically a Cloud Foundry MTA template that connects to an HDI container, creates the entities based on CDS syntax and maps them to an OData Service.

      This OData service can then be enhanced with your own methods - just like what you are able to achieve with Spring JPA Repositories.

      Best regards,
      Ivan

      Author's profile photo Ivan Mirisola
      Ivan Mirisola

      Hi Saji,

      According the the results you presented, there doesn't seem to be anything wrong with your code.

      I looks as though the Collection itself is empty. Did you check if the database tables in HANA are empty?

      Regards,
      Ivan

      Author's profile photo Saji Surendran
      Saji Surendran

      Hello Santosh,

      Do you have an answer for Ivan Mirisola's question? I too needed an example code for the same.

      Regards

      Saji S.

       

      Author's profile photo Riley Rainey
      Riley Rainey

      Saji and Ivan,

      Regarding your question about customizations, Olingo's JPA APIs provide a relatively direct mapping from the Java JPA objects you specify into an OData EDM model.  It is possible to extend that basic model by exposing one or more custom OData Function Imports.

      The code is outlined here: https://olingo.apache.org/doc/odata2/tutorials/jpafunctionimport.html

      I'd be interested to know if that model fits for you.  Let us know.

      Riley

      Author's profile photo Riley Rainey
      Riley Rainey

      Hi Santosh,

      I read this article with a lot of interest, as I've used Olingo in Java EE projects previously and I recently have made the switch to Spring.  I did get a number of useful tips from your code.

      It's worth noting that there's a fairly extensive production example of the same in the SAP Mentor's "Lemonaid" application. The source code is published on GitHub: https://github.com/sapmentors/lemonaid.

      If anyone else is attacking this problem, they might want to take the time to inspect that code sample as well.

      Riley

      Author's profile photo Elvis Garcia
      Elvis Garcia
      hi @Riley Rainey,
      I have a project similar to the structure of your Lemonaid project.
      
      When the WAR is uploaded (Deploy application) to SCP it shows us this error.
      
      Could you help us why this error is due?
      
      
      The error is only displayed when consulting the metadata (../odata.svc/$metadata) of the service.
      
      
      
      2020 09 25 01:07:05#+00#ERROR#com.csti.buenaventura.global.config.odata.CustomErrorCallback##P2001817462#https-jsse-nio-8041-exec-6#na#h69qfowpu6#licycon2#web#h69qfowpu6#na#na#na#na#Error in the OData. Reason: An exception occurred.
      org.apache.olingo.odata2.core.ep.EntityProviderProducerException: An exception occurred.
      at org.apache.olingo.odata2.core.ep.producer.XmlMetadataProducer.writeMetadata(XmlMetadataProducer.java:444)
      at org.apache.olingo.odata2.core.edm.provider.EdmServiceMetadataImplProv.getMetadata(EdmServiceMetadataImplProv.java:79)
      at org.apache.olingo.odata2.api.processor.ODataSingleProcessor.readMetadata(ODataSingleProcessor.java:382)
      at org.apache.olingo.odata2.core.Dispatcher.dispatch(Dispatcher.java:182)
      at org.apache.olingo.odata2.core.ODataRequestHandler.handle(ODataRequestHandler.java:131)
      at org.apache.olingo.odata2.core.rest.ODataSubLocator.handle(ODataSubLocator.java:164)
      at org.apache.olingo.odata2.core.rest.ODataSubLocator.handleGet(ODataSubLocator.java:58)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:498)
      at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:180)
      at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96)
      at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:189)
      at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:260)
      at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:99)
      at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59)
      at org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96)
      at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
      at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
      at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:253)
      at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:234)
      at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:208)
      at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:160)
      at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:180)
      at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:298)
      at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doGet(AbstractHTTPServlet.java:222)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
      at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:273)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
      at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
      at com.sap.core.communication.server.CertValidatorFilter.doFilter(CertValidatorFilter.java:158)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
      at com.sap.cloud.sdk.cloudplatform.servlet.RequestContextServletFilter.lambda$doFilter$0(RequestContextServletFilter.java:197) at com.sap.cloud.sdk.cloudplatform.servlet.RequestContextCallable.call(RequestContextCallable.java:131) at com.sap.cloud.sdk.cloudplatform.servlet.RequestContextServletFilter.doFilter(RequestContextServletFilter.java:209) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.servlet.resource.ResourceUrlEncodingFilter.doFilter(ResourceUrlEncodingFilter.java:64) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:92) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:128) at org.springframework.boot.web.servlet.support.ErrorPageFilter.access$000(ErrorPageFilter.java:66) at org.springframework.boot.web.servlet.support.ErrorPageFilter$1.doFilterInternal(ErrorPageFilter.java:103) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:121) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
      at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
      at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
      at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
      at com.sap.core.connectivity.jco.session.ext.RequestTracker.invoke(RequestTracker.java:55)
      at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:668)
      at com.sap.cloud.runtime.impl.bridge.security.AbstractAuthenticator.invoke(AbstractAuthenticator.java:206)
      at com.sap.cloud.runtime.kotyo.tomcat.support.CookieValve.invoke(CookieValve.java:37)
      at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
      at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
      at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
      at com.sap.core.tenant.valve.TenantValidationValve.invokeNextValve(TenantValidationValve.java:182)
      at com.sap.core.tenant.valve.TenantValidationValve.invoke(TenantValidationValve.java:97)
      at com.sap.js.statistics.tomcat.valve.RequestTracingValve.callNextValve(RequestTracingValve.java:113)
      at com.sap.js.statistics.tomcat.valve.RequestTracingValve.invoke(RequestTracingValve.java:59)
      at com.sap.core.js.monitoring.tomcat.valve.RequestTracingValve.invoke(RequestTracingValve.java:27)
      at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
      at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
      at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
      at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:615)
      at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
      at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:818)
      at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1626)
      at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
      at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
      at java.lang.Thread.run(Thread.java:836) Caused by: javax.xml.stream.XMLStreamException: prefix cannot be null or empty
      at com.sun.xml.internal.stream.writers.XMLStreamWriterImpl.writeAttribute(XMLStreamWriterImpl.java:625)
      at org.apache.olingo.odata2.core.ep.producer.XmlMetadataProducer.writeAnnotationAttributes(XmlMetadataProducer.java:624)
      at org.apache.olingo.odata2.core.ep.producer.XmlMetadataProducer.writeAnnotationElements(XmlMetadataProducer.java:662)
      at org.apache.olingo.odata2.core.ep.producer.XmlMetadataProducer.writeMetadata(XmlMetadataProducer.java:432)
      ... 85 common frames omitted