2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SAP CP Trial環境でMulti Target Applicationを作ってみる【ODataService_V2の作成】

Last updated at Posted at 2020-06-30

はじめに

以前の記事でSAP CloudPlatform Trial環境でのMTAプロジェクト(Multi Target Application)を作成し、
DBModuleやJavaModuleの追加をしました。

【DBModuleの追加】
 SAP CP Trial環境でMulti Target Applicationを作ってみる【DBModuleの作り方】

【JavaModuleの追加】
SAP CP Trial環境でMulti Target Applicationを作ってみる【JavaアプリとDBModuleの連携】

今回はJavaを使ってODataServiceををMTAプロジェクト内に作成してみます。

JavaModuleの追加(OData V2 Service)

ODataServiceを作るにあたって、
もともとの想定ではSpling Boot ApplicationとApache Olingoを使って
ODataServiceを作成しようと考えていました。
そのためJavaModuleの追加時にはSpling Boot Applicationを追加しましたが、
JavaModuleのテンプレートのなかにODataServiceが作れそうな選択肢が用意されているので
こちらから作成してみます。

図1.png

ドキュメントを参照するとSAP Cloud Platform SDK for service developmentがベースになっているとのことなので、
以下の一連のブログを参考としています。

【参考URL】
SAP Cloud Platform SDK for service development: Overview of Blogs

上記ではODataService V4を作成していますが、
今回はブログを参考にしつつ、ODataService V2を作成します。

Javaクラス、定義情報(Metadata)の追加

参考URLをもとにServiceImplementation.javaDemoService.xmlを追加します。

※ServiceImplementation.javaの追加
OData V2 ServiceをdemoODSv2というモジュール名で作成した場合、下図のようにsrc配下の最下層で右クリック -> New -> Java Class
image.png

起動する子画面にServiceImplementation`を入力してNext -> finish押下
image.png

ファイルの内容は以下の通り。

ServiceImplementation.java
package <作成したJavaClassのものを使用>;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sap.cloud.sdk.service.prov.api.operations.Query;
import com.sap.cloud.sdk.service.prov.api.operations.Read;
import com.sap.cloud.sdk.service.prov.api.request.QueryRequest;
import com.sap.cloud.sdk.service.prov.api.request.ReadRequest;
import com.sap.cloud.sdk.service.prov.api.response.ErrorResponse;
import com.sap.cloud.sdk.service.prov.api.response.QueryResponse;
import com.sap.cloud.sdk.service.prov.api.response.ReadResponse;


public class ServiceImplementation {	


	@Query(serviceName="DemoService", entity="People")
	public QueryResponse getPeople(QueryRequest request) {
		List<Map<String, Object>> peopleMap = getPeople();
		return QueryResponse.setSuccess().setDataAsMap(peopleMap).response();
	}

	
	@Read(serviceName="DemoService", entity="People")
	public ReadResponse getPerson(ReadRequest request) {
		
		// retrieve the requested person from URI 
		Map<String, Object> keyPredicates = request.getKeys();
		Object keyObject = keyPredicates.get("UniqueId");
		Integer id = (Integer)keyObject;
		
		// search the requested person in the database
		List<Map<String, Object>> peopleList = getPeople();
		Map<String, Object> requestedPersonMap = new HashMap<String, Object>();
		for(Map<String, Object> personMap : peopleList) {
			if(((Integer)personMap.get("UniqueId")).equals(id)) {
				// found it
				requestedPersonMap = personMap;
			}
		}		
		
		// handle error: "not found" 
		if(requestedPersonMap.isEmpty()) {
			Logger logger = LoggerFactory.getLogger(ServiceImplementation.class);
			logger.error("Person with UniqueId " + id + " doesn't exist! Service request was invoked with invalid key value"); 
			ErrorResponse response = ErrorResponse.getBuilder()
					.setMessage("Person with UniqueId " + id + " doesn't exist!")
					.setStatusCode(404)
					.response();
			return ReadResponse.setError(response);
		}
		
		return ReadResponse.setSuccess().setData(requestedPersonMap).response();
	}

	
	/* Dummy Database */
	
	private List<Map<String, Object>> getPeople(){
		List<Map<String, Object>> peopleMap = new ArrayList<Map<String, Object>>();
		
		peopleMap.add(createPerson(0, "Anna"));
		peopleMap.add(createPerson(1, "Berta"));
		peopleMap.add(createPerson(2, "Claudia"));
		peopleMap.add(createPerson(3, "Debbie"));
		
		return peopleMap;
	}
	
	private Map<String, Object> createPerson(Integer id, String name){
		Map<String, Object> personMap = new HashMap<String, Object>();
		
		personMap.put("UniqueId", id);
		personMap.put("Name", name);
		
		return personMap;
	}
	
}

※DemoService.xmlの追加
下図のようにresource/edmxで右クリック -> New -> File押下
File名にDemoService.xmlを入力してOK押下
image.png

ファイルの内容は以下の通り。

DemoService.xml
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
	<edmx:DataServices>
		<Schema Namespace="demo" xmlns="http://docs.oasis-open.org/odata/ns/edm">
			<EntityType Name="Person">
				<Key>
					<PropertyRef Name="UniqueId" />
				</Key>
				<Property Name="UniqueId" Type="Edm.Int32" />
				<Property Name="Name" Type="Edm.String" />
			</EntityType>
			<EntityContainer Name="container" >
				<EntitySet Name="People" EntityType="demo.Person" />
			</EntityContainer>
		</Schema>
	</edmx:DataServices>
</edmx:Edmx>

この段階のJavaではDBに接続せず、固定値のデータを作成しています

		peopleMap.add(createPerson(0, "Anna"));
		peopleMap.add(createPerson(1, "Berta"));
		peopleMap.add(createPerson(2, "Claudia"));
		peopleMap.add(createPerson(3, "Debbie"));

このままJavaアプリケーションをRunしてみました。
以下のURLにアクセスするとなんとなく動いている感じがする画面が表示されます。
https://<javaアプリのURL参照>.cfapps.eu10.hana.ondemand.com/odata/v2/

図2.png

ただ、表示されたODataServiceのURLにアクセスしても期待したODataのレスポンスは得られませんでした。

ちなみに、平行して確認していたOData V4では同様のの内容で動作することが確認できました。
図3.png
図4.png

調べるとDemoService.xmlがOData v4用の定義だったことが原因らしいので、
OData v2用にDemoService.xmlの中身を編集します。

DemoService.xml(v2)

<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
    <edmx:DataServices m:DataServiceVersion="2.0">
		<Schema Namespace="demo" xmlns="http://schemas.microsoft.com/ado/2008/09/edm">
			<EntityType Name="Person">
				<Key>
					<PropertyRef Name="UniqueId" />
				</Key>
				<Property Name="UniqueId" Type="Edm.Int32" />
				<Property Name="Name" Type="Edm.String" />
			</EntityType>
			<EntityContainer Name="container" m:IsDefaultEntityContainer="true">
				<EntitySet Name="People" EntityType="demo.Person" />
			</EntityContainer>
		</Schema>
    </edmx:DataServices>
</edmx:Edmx>

上記に差し替えをおこなったあと、再度Javaアプリケーションを実行します。
図6.png

OData V2が動作していることが確認できました。
URLの末尾に/Peopleを追加して仮データが参照できることも確認できます。
図7.png

上記まででJavaでのOData v2が動作することが確認できたので、
DBから値を取得できるようにJavaのソースを改修します。

以前作成したSpling Boot Applicationのソースコードを参考にして、
DBとの接続を試します。

以下のコードではリクエストしたらDBに接続し、
成功した場合はJavaのログに以下を表示するようにしてみます。
!!!! GET CURRENT !!!! <スキーマ名>

ServiceImplementation.java
package com.company.javatest.ODSv2;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sap.cloud.sdk.service.prov.api.operations.Query;
import com.sap.cloud.sdk.service.prov.api.operations.Read;
import com.sap.cloud.sdk.service.prov.api.request.QueryRequest;
import com.sap.cloud.sdk.service.prov.api.request.ReadRequest;
import com.sap.cloud.sdk.service.prov.api.response.ErrorResponse;
import com.sap.cloud.sdk.service.prov.api.response.QueryResponse;
import com.sap.cloud.sdk.service.prov.api.response.ReadResponse;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.json.JSONArray;
import org.json.JSONObject;
import java.sql.DriverManager;

public class ServiceImplementation {	


	@Query(serviceName="DemoService", entity="People")
	public QueryResponse getPeople(QueryRequest request) {
		List<Map<String, Object>> peopleMap = getPeople();
		return QueryResponse.setSuccess().setDataAsMap(peopleMap).response();
	}

	
	@Read(serviceName="DemoService", entity="People")
	public ReadResponse getPerson(ReadRequest request) {
		
		// retrieve the requested person from URI 
		Map<String, Object> keyPredicates = request.getKeys();
		Object keyObject = keyPredicates.get("UniqueId");
		Integer id = (Integer)keyObject;
		
		// search the requested person in the database
		List<Map<String, Object>> peopleList = getPeople();
		Map<String, Object> requestedPersonMap = new HashMap<String, Object>();
		for(Map<String, Object> personMap : peopleList) {
			if(((Integer)personMap.get("UniqueId")).equals(id)) {
				// found it
				requestedPersonMap = personMap;
			}
		}		
		
		// handle error: "not found" 
		if(requestedPersonMap.isEmpty()) {
			Logger logger = LoggerFactory.getLogger(ServiceImplementation.class);
			logger.error("Person with UniqueId " + id + " doesn't exist! Service request was invoked with invalid key value"); 
			ErrorResponse response = ErrorResponse.getBuilder()
					.setMessage("Person with UniqueId " + id + " doesn't exist!")
					.setStatusCode(404)
					.response();
			return ReadResponse.setError(response);
		}
		
		return ReadResponse.setSuccess().setData(requestedPersonMap).response();
	}
	
	/* Connection DataBase */
	private List<Map<String, Object>> getPeople(){
		List<Map<String, Object>> peopleMap = new ArrayList<Map<String, Object>>();
		try {
			Connection conn = getConnection();
			if (conn != null) {
				String currentUser = "";
				PreparedStatement prepareStatement = conn.prepareStatement("SELECT CURRENT_USER \"current_user\" FROM DUMMY;");
				ResultSet resultSet = prepareStatement.executeQuery();
				int column = resultSet.findColumn("current_user");
				while (resultSet.next()) {
					// peopleMap.add(createPerson(resultSet.findColumn("ID"), resultSet.findColumn("NAME")));
				  currentUser += resultSet.getString(column);
				}
				System.out.println("!!!! GET CURRENT !!!!" + currentUser);

				peopleMap = getDummyPeople();
			} else {
				// builder.append("no");
				peopleMap = getDummyPeople();
			}
		} catch (SQLException e) {
			// builder.append("no");
			System.out.println("SQL Exception");
		}
		
		return peopleMap;
	}
	private Connection getConnection() {
		Connection conn = null;
		String DB_USERNAME = "";
		String DB_PASSWORD = "";
		String DB_HOST = "";
		String DB_PORT = "";
		try {
			JSONObject obj = new JSONObject(System.getenv("VCAP_SERVICES"));
			JSONArray arr = obj.getJSONArray("hanatrial");
			DB_USERNAME = arr.getJSONObject(0).getJSONObject("credentials").getString("user");
			DB_PASSWORD = arr.getJSONObject(0).getJSONObject("credentials").getString("password");
			DB_HOST = arr.getJSONObject(0).getJSONObject("credentials").getString("host").split(",")[0];
			DB_PORT = arr.getJSONObject(0).getJSONObject("credentials").getString("port");
			 // String DB_READ_CONNECTION_URL = "jdbc:sap://" + DB_HOST + ":" + DB_PORT;
			String DB_READ_CONNECTION_URL = arr.getJSONObject(0).getJSONObject("credentials").getString("url");
			conn = (Connection) DriverManager.getConnection(DB_READ_CONNECTION_URL, DB_USERNAME, DB_PASSWORD);
		} catch (Exception e) {
        	System.out.println("Connection Error");
		}
		return conn;
	}
	/* Dummy Database */
	private List<Map<String, Object>> getDummyPeople(){
		List<Map<String, Object>> peopleMap = new ArrayList<Map<String, Object>>();
		
		peopleMap.add(createPerson(0, "Anna"));
		peopleMap.add(createPerson(1, "Berta"));
		peopleMap.add(createPerson(2, "Claudia"));
		peopleMap.add(createPerson(3, "Debbie"));
		
		return peopleMap;
	}
	
	private Map<String, Object> createPerson(Integer id, String name){
		Map<String, Object> personMap = new HashMap<String, Object>();
		
		personMap.put("UniqueId", id);
		personMap.put("Name", name);
		
		return personMap;
	}
	
}

上記のソースコードに置き換えてJava Applicationを再実行し、
もう一度/odata/v2/DemoService/Peopleをリクエストします。

以下のようにログが出力されたのでDBとの接続はできているみたいです。
image.png

DBインスタンスにデータを作成する

WebIDEから実行したJava Applicationが参照しているDBインスタンスにテストデータを作成します。

実行中のURLを参考に、コクピット画面からSpace内に存在するApplicationの詳細を確認します。
右側メニューからService Bindingsを選択します。
image.png

JavaインスタンスにバインドされているDBインスタンスの名称がわかります。

image.png

上記でインスタンス名称がわかったら、以下赤枠を押下し、
WebIDEのDataBase Explorerを表示します。
image.png

「Add DataBase」で対象のDBを選択してOKを押下します
image.png

DBとの接続ができるとTable等確認ができますが、
まだ何も作成されていないため、Tablesを選択しても何も出てきません。
なので左上にあるSQLコンソールからCreate文を実行します。

image.png

create table people (UniqueId SMALLINT CS_INT not null primary key, Name varchar(50));

上記のSQLを実行し、Peopleテーブルを作成しました。
image.png

右上あたりのOpen Dataを押下するとGUIでデータを作れるので、
以下のようなテストデータを作成します。

image.png

これでおそらくJavaからデータが取得できると思われます。

JavaからDBのデータを取得する

先ほど作成したJavaファイルの中身を変更し、Peopleテーブルに作ったデータを呼び出してみます。
ダミーデータを作成していた関数:getPeopleの内容を以下のように変更します。

ServiceImplementation.java
	/* Connection DataBase */
	private List<Map<String, Object>> getPeople(){
		List<Map<String, Object>> peopleMap = new ArrayList<Map<String, Object>>();
		try {
			Connection conn = getConnection();
			if (conn != null) {
				String currentUser = "";
				PreparedStatement prepareStatement = conn.prepareStatement("SELECT * FROM PEOPLE;");
				ResultSet resultSet = prepareStatement.executeQuery();
				int column_id = resultSet.findColumn("UNIQUEID");
				int column_name = resultSet.findColumn("NAME");
				while (resultSet.next()) {
					peopleMap.add(createPerson(Integer.valueOf(resultSet.getString(column_id)), resultSet.getString(column_name)));
				  //currentUser += resultSet.getString(column);
				}
				// System.out.println("!!!! GET CURRENT !!!!" + currentUser);

				// peopleMap = getDummyPeople();
			} else {
				// builder.append("no");
				peopleMap = getDummyPeople();
			}
		} catch (SQLException e) {
			// builder.append("no");
			System.out.println("SQL Exception");
		}
		
		return peopleMap;
	}

Java Applicationを再実行し、
もう一度/odata/v2/DemoService/Peopleをリクエストします。
以下のように、Peopleテーブルに作成したデータが取得できるようになりました!

image.png

/odata/v2/DemoService/People(1)など、
UniqueIDを指定して絞り込みもできています。

image.png

ただ、上記サンプルのソースコードではハードコーディングのテストデータを絞り込む処理をそのまま流用しているので、
SQLでJava側に全レコードを取得した後に一致するIDのレコードのみに絞り込む動作をしています。
改善が必要です。

また、$filterなどのクエリオプションに関しても実装できていないため、
ここからJavaの作りこみが必要になると思います。

おわりに

Java Applicationの実装に関してはODataとして不足している機能が多いですが、
ひとまず当記事の内容で
 DB ⇔ OData(Java)
までは実装できたかと思います。

次はODataをFioriアプリケーション(UIモジュール)から呼び出せるようにしたいと思います。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?