このトピックでは、エンドツーエンド処理の例を示します。UI プログラムはバック・エンドにあります。フロントエンドには、データ・グリッドを表示する Rich UI アプリケーションがあります。この 2 つのテクノロジーの背景情報については、『UI プログラムおよびゲートウェイ・サービス』および『Rich UI の DataGrid および DataGridTooltip』を参照してください。
Rich UI アプリケーションの初期外観を次に示します。

要約すると、データ・ローダーが初期ページを要求し、UI プログラムがすべてのページに必要なレコードを取得し、最初のページのレコードと合計カウントを返します。データ・ローダーがユーザーのクリック操作に応答し、必要に応じて追加ページを提供するために UI プログラムを呼び出します。ユーザーがこれまでに表示されていない最終ページを要求すると、データ・ローダーはページ番号 -1 を要求します。これにより、UI プログラムが終了します。
.EglRuiDataGridHeaderCell {
text-align: center;
}
すべての開発作業が完了するまでは、Rich UI アプリケーションをターゲット・プロジェクトにデプロイする必要はありません。
プロジェクトを適切に生成するには、genProject ビルド記述子オプションがターゲット・プロジェクトを参照することを指定します。また、デプロイメント記述子に、ゲートウェイ REST サービスをデプロイするための項目が含まれており、「Web サービス・デプロイメント」タブの「ステートフル」チェック・ボックスにチェック・マークを付けていることを確認します。
http://localhost:8080/MyTargetProject/MyRichUIHandler-en_US.html
このアドレスが適切であるのは、プロジェクトをワークステーションにデプロイしており、アプリケーション・サーバーがポート 8080 で listen しており、ターゲット・プロジェクト名が MyTargetProject、Rich UI ハンドラー名が MyRichUIHandler であり、HTML ファイルの構成でロケールが米国英語に設定されている場合です。
データベース・スキーマはサンプルでのみ有効です。例えば、キー制約は存在しません。
CREATE TABLE APP.CAR_MASTER
(
VIN CHAR(17) NOT NULL,
Make VARCHAR(15) NOT NULL,
Model VARCHAR(15) NOT NULL,
TheYear CHAR(4) NOT NULL
);
CREATE TABLE APP.CAR_PURCHASES
(
VIN CHAR(17) NOT NULL,
PURCHASE_DATE DATE NOT NULL,
COST DECIMAL(8,2) NOT NULL
);
CREATE TABLE APP.CAR_SALES
(
VIN CHAR(17) NOT NULL,
SALE_DATE DATE NOT NULL,
PRICE DECIMAL(8,2) NOT NULL
);
INSERT INTO APP.CAR_MASTER VALUES
( '11111111111111111', 'Honda', 'Accord', '1998'),
( '22222222222222222', 'Ford', 'Mustang', '2006'),
( '33333333333333333', 'Chevrolet', 'Camaro', '2010'),
( '44444444444444444', 'Toyota', 'RAV4', '2008'),
( '55555555555555555', 'Triumph', 'Spitfire', '1980'),
( '66666666666666666', 'BMW', '328XI', '2007'),
( '77777777777777777', 'Cadillac', 'Escalade', '2004'),
( '88888888888888888', 'Chrysler', 'Sebring', '2006'),
( '99999999999999999', 'Lexus', 'ES 300', '2009'),
( 'AAAAAAAAAAAAAAAAA', 'Honda', 'Civic', '2008'),
( 'BBBBBBBBBBBBBBBBB', 'Toyota', 'Celica', '2005');
INSERT INTO APP.CAR_PURCHASES VALUES
( '11111111111111111', '2004-06-15', 4000.00),
( '22222222222222222', '2008-07-26', 7200.00),
( '33333333333333333', '2010-11-11', 21000.00),
( '44444444444444444', '2009-02-02', 18000.00),
( '55555555555555555', '2007-01-04', 20500.00),
( '66666666666666666', '2010-08-08', 26900.00),
( '77777777777777777', '2010-04-04', 17200.00),
( '88888888888888888', '2009-11-06', 11400.00),
( '99999999999999999', '2009-12-07', 37000.00),
( 'AAAAAAAAAAAAAAAAA', '2010-11-12', 13000.00),
( 'BBBBBBBBBBBBBBBBB', '2008-03-03', 8300.00);
INSERT INTO APP.CAR_SALES VALUES
( '11111111111111111', '2004-11-18', 4800.00),
( '22222222222222222', '2009-01-12', 7000.00),
( '33333333333333333', '2010-11-14', 22000.00),
( '44444444444444444', '2009-02-03', 19200.00),
( '55555555555555555', '2007-02-18', 23500.00),
( '66666666666666666', '2010-09-20', 28000.00),
( '77777777777777777', '2010-05-06', 18500.00),
( '88888888888888888', '2009-11-06', 11400.00),
( '99999999999999999', '2010-02-07', 37200.00),
( 'AAAAAAAAAAAAAAAAA', '2010-11-16', 13800.00),
( 'BBBBBBBBBBBBBBBBB', '2008-04-05', 7400.00);
次に UI プログラム例を示します。
package myPkg;
record CarStatus type SQLRecord {
tableNames =[["APP.CAR_MASTER", "A"],
["APP.CAR_PURCHASES", "B"],["APP.CAR_SALES", "C"]],
defaultSelectCondition =
#sqlCondition{ A.VIN = B.VIN AND A.VIN = C.VIN ORDER BY VIN},
keyItems =[VIN]}
VIN string{column = "A.VIN"};
MAKE string;
MODEL string;
YEAR string{column = "A.THEYEAR"};
PURCHASE_DATE date;
COST decimal(8, 2);
SALE_DATE date;
PRICE decimal(8, 2);
end
// リクエスターからの初期ページ・サイズ
Record PageSizeContainer
pageSize INT;
end
Record MyException type Exception end
Record SendListContainer
// 使用可能なレコードの数
numberOfRecords INT;
pageNumber INT = 1;
sendList CarStatus[]{};
end
program MyUIProgram type UIProgram
{ inputUIRecord = myPageSizeContainer, segmented = true }
const MAXIMUM_DATABASE_ROWS INT = 100;
myPageSizeContainer PageSizeContainer;
function main()
tentativeCarListSize INT;
numberToSend INT;
numberOnLastPage INT;
sendCounter, startRowOnPage, endRowOnPage INT;
numberOfAvailablePages INT;
mySendListContainer SendListContainer;
myCarList CarStatus[]{};
i int;
if (myPageSizeContainer.pageSize <= 0)
throw new MyException{ message =
"Your page size is not valid. You requested " + numberToSend + "."};
end
tentativeCarListSize = getNumberofCars();
if (tentativeCarListSize > MAXIMUM_DATABASE_ROWS || tentativeCarListSize == 0)
throw new MyException{message =
"The number of available rows is not valid. Cannot return " +
tentativeCarListSize + " rows."};
end
// 取得したデータベース行を使用して配列をセットアップする。
try
get myCarList;
SQLLib.disconnect();
onException(except AnyException)
throw new MyException{message = "Problem in data access. "
+ except.message};
end
// 現時点では、行が多すぎる場合には例外はスローされない
mySendListContainer.numberOfRecords = myCarList.getSize();
numberOfAvailablePages =
MathLib.Ceiling(mySendListContainer.numberOfRecords /
myPageSizeContainer.pageSize);
numberOnLastPage = mySendListContainer.numberOfRecords %
myPageSizeContainer.pageSize;
if (numberOnLastPage == 0)
numberOnLastPage = myPageSizeContainer.pageSize;
end
while (mySendListContainer.pageNumber != -1)
// 送信する要素の数のための配列をセットアップする
if (mySendListContainer.pageNumber < numberOfAvailablePages)
numberToSend = myPageSizeContainer.pageSize;
else
numberToSend = numberOnLastPage; // 最終ページ
end
for (sendCounter from 1 to numberToSend)
mySendListContainer.sendList.appendElement(new CarStatus{});
end
// 送信するデータベース・レコードを指定する
if (mySendListContainer.pageNumber == 1)
startRowOnPage = 1;
else
startRowOnPage = (mySendListContainer.pageNumber - 1) *
myPageSizeContainer.pageSize + 1;
end
if (mySendListContainer.pageNumber == numberOfAvailablePages )
endRowOnPage = startRowOnPage + numberOnLastPage - 1;
else
endRowOnPage = startRowOnPage + myPageSizeContainer.pageSize - 1;
end
// 送信するデータベース・レコードをコピーする
i = startRowOnPage;
for (n int from 1 to numberToSend)
mySendListContainer.sendList[n] = myCarList[i];
i = i + 1;
end
converse mySendListContainer;
mySendListContainer.sendList.removeAll();
end
end // main() を終了する
function getNumberOfCars() returns(int)
numberOfRows INT;
countQuery STRING = "Select count(*) from APP.CAR_MASTER";
try
prepare myTest from countQuery;
get with myTest into numberOfRows;
onException(except AnyException)
throw new MyException{message =
"Problem in accessing the row count. " + except.message}; end
return(numberOfRows);
end
end
次に Rich UI アプリケーションの例を示します。
package myRichUIPkg;
import com.ibm.egl.rui.widgets.DataGrid;
import com.ibm.egl.rui.widgets.DataGridColumn;
import com.ibm.egl.rui.widgets.DataGridLib;
import egl.ui.gateway.UIGatewayRecord;
record CarStatusBasic type SQLRecord
VIN string;
MAKE string;
MODEL string;
YEAR string;
PURCHASE_DATE date;
COST decimal(8, 2);
SALE_DATE date;
PRICE decimal(8, 2);
end
record PageSizeContainer
pageSize int;
end
// フィールド名は JSON 文字列のエントリーと一致している必要がある
record CarStatusBasicContainer
numberOfRecords int;
pageRequested int{JSONName = "pageNumber"} = 1;
sendList CarStatusBasic[]{};
end
handler MyRichUIHandler type RUIhandler
{initialUI =[grid], onConstructionFunction = start,
cssFile = "css/RichUIProject.css", title = "View Car Sales"}
grid DataGrid{pageSize = PAGE_SIZE, showButtonBar = true, style = "overflow:auto",
columns =[
new DataGridColumn{name = "VIN", displayName = "VIN", width = 135,
alignment = DataGridLib.ALIGN_CENTER},
new DataGridColumn{name = "MAKE", displayName = "Make",
alignment = DataGridLib.ALIGN_CENTER},
new DataGridColumn{name = "Model", displayName = "Model",
alignment = DataGridLib.ALIGN_CENTER},
new DataGridColumn{name = "Year", displayName = "Year",
alignment = DataGridLib.ALIGN_CENTER},
new DataGridColumn{name = "Purchase_Date", displayName = "Purchase Date",
alignment = DataGridLib.ALIGN_CENTER},
new DataGridColumn{name = "Cost", displayName = "Purchase Price",
alignment = DataGridLib.ALIGN_RIGHT, width=90},
new DataGridColumn{name = "Sale_Date", displayName = "Sale Date",
alignment = DataGridLib.ALIGN_CENTER},
new DataGridColumn{name = "Price", displayName = "Sale Price",
alignment = DataGridLib.ALIGN_RIGHT, width =80},
new DataGridColumn{name = "Profit", displayName = "Profit (Loss)",
formatters =[formatProfit], alignment = DataGridLib.ALIGN_RIGHT, width = 80}]};
const PAGE_SIZE int = 5;
myPageSizeContainer PageSizeContainer;
listContainer CarStatusBasicContainer{};
firstInvocation boolean;
gridDataList CarStatusBasic[1]{};
requestedPage int = 1;
pagesRequested boolean[]{};
handledEarlier boolean;
numberOfAvailablePages int;
gateRec UIGatewayRecord{};
allLoaded boolean;
gatewayServiceVar UIGatewayService{@BindService{bindingKey = "UIGatewayService"}};
function start()
gateRec.uiProgramName = "myPkg.MyUIProgram";
myPageSizeContainer.pageSize = PAGE_SIZE;
StrLib.defaultDateFormat = "yyyy-MM-dd";
pagesRequested.appendElement(false);
firstInvocation = true;
allLoaded = false;
// 本資料の編集段階では、すべてのデータがロードされているる場合にのみソートが使用可能である
grid.enableSorting = false;
grid.dataLoader = myDataLoader;
grid.data = gridDataList as any[];
end
function formatProfit(class string inOut, value string inOut, rowData any in)
calculation decimal(8, 2) =
rowData.Price as decimal(8, 2) - rowData.Cost as decimal(8, 2);
if(calculation < 0)
calculation = -calculation;
value = "(" + calculation + ")";
else
value = calculation as string;
end
end
function myDataLoader(startRow int in, endRow int in, sortFieldName string in,
sortDirection int in) returns(boolean)
if(allLoaded)
return(true);
end
if(firstInvocation)
if(pagesRequested[1] == true)
handledEarlier = true;
else
handledEarlier = false;
pagesRequested[1] = true;
gateRec.data = ServiceLib.convertToJSON(myPageSizeContainer);
call gatewayServiceVar.invokeProgram(gateRec)
returning to callbackFunc onException handleException;
end
else
requestedPage = mathLib.ceiling(startRow /
myPageSizeContainer.pageSize);
if(pagesRequested[requestedPage] == true)
handledEarlier = true;
else
handledEarlier = false;
listContainer.pageRequested = requestedPage;
pagesRequested[requestedPage] = true;
gateRec.data = ServiceLib.convertToJSON(listContainer);
call gatewayServiceVar.invokeProgram(gateRec)
returning to callbackFunc onException handleException;
end
end
return(handledEarlier);
end
function callbackFunc(gatewayRecord uigatewayrecord in)
i, n, startUpdate, endUpdate int;
ServiceLib.convertFromJSON(gatewayRecord.data, listContainer);
numberOfReturns int = listContainer.sendList.getSize();
allLoaded = true;
// pagesRequested 配列をセットアップできるのは、
// UI プログラムからの使用可能なレコードの数を確認した後のみである
if(firstInvocation)
numberOfAvailablePages =
MathLib.Ceiling(listContainer.numberOfRecords /
myPageSizeContainer.pageSize);
if(numberOfAvailablePages > 1)
for(i from 2 to numberOfAvailablePages)
pagesRequested.appendElement(false);
end
end
// グリッドのデータ配列をセットアップする
for(i from 2 to listContainer.numberOfRecords)
GridDataList.appendElement(new CarStatusBasic);
end
firstInvocation = false;
end
// UI プログラムの使用を終了するかどうか
for(i from 1 to numberOfAvailablePages)
if(pagesRequested[i] == false)
allLoaded = false;
end
end
if(allLoaded)
grid.enableSorting = true;
listContainer.pageRequested = -1;
gatewayRecord.data = ServiceLib.convertToJSON(listContainer);
call gatewayServiceVar.invokeProgram(gatewayRecord)
returning to handleServiceEnd onException handleException;
end
// データのロード
if(requestedPage == 1)
for(i from 1 to numberOfReturns)
gridDataList[i] = listContainer.sendList[i];
end
else
startUpdate = (requestedPage - 1) * myPageSizeContainer.pageSize + 1;
endUpdate = startUpdate + numberOfReturns - 1;
n = 1;
for(i from startUpdate to endUpdate)
gridDataList[i] = listContainer.sendList[n];
n = n + 1;
end
end
grid.data = gridDataList as any[];
grid.render();
end
function handleException(exp AnyException in)
SysLib.writeStdOut(exp.message);
grid.cancelDataLoader();
end
function handleServiceEnd(gateRec uiGatewayRecord in)
if(!gateRec.terminated)
SysLib.writeStdOut("Error: The program is still running.");
grid.render();
end
end
end