17Th
Developer Camp【T4】 C++Builderテクニカルセッション「C++BuilderによるWebサービス&マルチスレッド対応リソースプー
ルの設計」

エンバカデロ・テクノロジーズ
エヴァンジェリスト
高橋智宏

1アジェンダ• Webサービス（CGI版）– BASIC認証（IIS）• SOAPクライアント– WSDLインポータ, BASIC認証対応• Webサービス（スタンドアロン版）– 独自のBASIC認証を実装• リソースプール– データモジュール（データベース接続）– Win32 API– POSIXスレッド– Boost::Thread2

17Th
Developer Camp

1

Webサービスの構築

3

CGI版のWebサービス• [C++Builderプロジェクト]-[Webサービス]-[SOAPサー
バーアプリケーション]– CGI実行形式• [C++Builderプロジェクト]-[Webサービス]-[SOAPサー
バーインターフェイス]– サービス名: MyService– サンプルメソッドの生成: On••

注意点1: プロジェクトソース(例:Project1.cpp)が少し壊れることがある
注意点2:サンプルのTSampleStructクラスのメンバがAnsiString型
4

Webサービスメソッドの実装のポイント• echoStructメソッドの実装– TRemotableの派生クラスを処理する– クラスのインスタンスのconstポインタを受け取り– クラスのインスタンスのポインタを返す• returnしたインスタンスは後でdeleteされる• NULLを返すのも一応OK• 例外はすべて捕捉して伝播させない（SOAPの例外機構は貧弱なので）TSampleStruct* TMyServiceImpl::echoStruct(const TSampleStruct* pEmployee){
TSampleStruct* retval = NULL;
try {retval = new TSampleStruct();
retval->LastName = pEmployee->LastName;retval->FirstName = pEmployee->FirstName;retval->Salary = pEmployee->Salary;}catch (...) {; // do something}return retval;}5

IISにCGIを配布 － BASIC認証• CGIを利用できるように設定– ハンドラマッピング, CGIの制限 etc…

• WebサイトでBASIC認証を有効にする– 基本認証（HTTP 401チャレンジ）: 有効• 既定のドメイン：• 領域（Realm,レルム）： 例として test を設定– 匿名認証: 無効• WSDLを確認– Webブラウザ

6

17Th
Developer Camp

2

SOAPクライアントの構築

7

WSDLインポータ• C++アプリケーションプロジェクトの作成• [C++Builderプロジェクト]-[Webサービス]-[WSDLインポータ]– WSDLソース– 認証: ユーザー名– 認証: パスワード• 自動生成されるファイル– IMyService.h– IMyService.cpp• プロキシの取得– GetIMyService関数
8クライアント側でのBASIC認証への対応• THTTPRIOクラスのHTTPWebNodeプロパティを使用– UserNameプロパティ– Passwordプロパティ•注意: C++Builder XE（Delphi XE）では、必要なら、認証ダイアログを自前で用意して表示させる必要がある– 以前のバージョンでは、自動で表示されてしまっていたので要注意• QC#91848
9クライアントコードの実装のポイント － その１• プロキシの取得は1回だけ– メモリリークあり： QC#91160– THTTPRIOクラスのインスタンスは自前で用意• BASIC認証のユーザー名・パスワードを設定• deleteは不要

10クライアントコードの実装のポイント － その２•

Webサービスメソッドの呼び出しは常に失敗する可能性がある– try … catch を忘れずに!!

• Webサービスメソッドの戻り値のNULLチェック• パラメータと戻り値のそれぞれのインスタンスをdelete– try … __finally を忘れずに!!

11

17Th
Developer Camp3

スタンドアロン版
Webサービスサーバー

12

スタンドアロン版のWebサービス• [C++Builderプロジェクト]-[Webサービス]-[SOAPサー
バーアプリケーション]– Indy VCL（or コンソール） アプリケーションを選択• [C++Builderプロジェクト]-[Webサービス]-[SOAPサー
バーインターフェイス]– サービス名: MyService– サンプルメソッドの生成: On••

注意点1: プロジェクトソース(例:Project1.cpp)が少し壊れることがある
注意点2:サンプルのTSampleStructクラスのメンバがAnsiString型
13

サービスアクティベーションモデル
• 単一プロセスのWebサーバーなので、クラスインスタンスのライフサイクルに注意が必要• グローバル：単一インスタンスですべて処理（スレッドセーフ必須）• 要求ごと：処理のたびに new & delete が繰り返される

14

スタンドアロン版 － BASIC認証• メインフォームで使用されているIndyクラスがポイント– TIdHTTPWebBrokerBridge• DoCommandGet, DoCommandOther がHTTPリクエストを処理している• TIdHTTPWebBrokerBridgeの派生クラスに取り替える– DoCommandGet, DoCommandOtherをオーバーライド– BASIC認証を実装– メインフォームのOnCreateイベントで new するvoid __fastcall TForm1::FormCreate(TObject *Sender){//FServer = new TIdHTTPWebBrokerBridge(this);
FServer = new TIdHTTPWebBrokerBridgeEx(this);}
15

スタンドアロン版 － BASIC認証（続き）• TIdHTTPWebBrokerBridgeExクラスの実装#include <IdContext.hpp>#include <IdCustomHTTPServer.hpp>
class TIdHTTPWebBrokerBridgeEx : public TIdHTTPWebBrokerBridge {public:__fastcall TIdHTTPWebBrokerBridgeEx(TComponent* AOwner) :TIdHTTPWebBrokerBridge(AOwner){}__fastcall virtual ~TIdHTTPWebBrokerBridgeEx(){}protected:
void __fastcall DoCommandGet(TIdContext* AThread,TIdHTTPRequestInfo* ARequestInfo, TIdHTTPResponseInfo* AResponseInfo) {if( ARequestInfo->AuthExists &&ARequestInfo->AuthUsername == "ken" &&ARequestInfo->AuthPassword == "password" ) {TIdHTTPWebBrokerBridge::DoCommandGet(AThread, ARequestInfo,AResponseInfo);
return;}AResponseInfo->ResponseNo = 401;
AResponseInfo->ContentText = "auth error";
AResponseInfo->AuthRealm= "test";}
void __fastcall DoCommandOther(TIdContext* AThread,TIdHTTPRequestInfo* ARequestInfo, TIdHTTPResponseInfo* AResponseInfo) {DoCommandGet(AThread, ARequestInfo, AResponseInfo);
16}
};

17Th
Developer Camp

4

マルチスレッドへの対応
リソースプールの設計

17

有限個のリソースで対応する• CGIはパフォーマンスの点では劣る• スタンドアロン版– サービスアクティベーションモデルに関係なく、コネクションプール（orデータモジュールのプール）が必要– Webサービスメソッド内では…
1. プールからリソースを借りて2. リソースを利用して3. プールにリソースを返す
クラス
インスタンスクラス
インスタンスクラス
インスタンスクラス
インスタンス

リクエスト
レスポンス

クライアント接続
クエリー実行
切断

Webサーバ

RDBMS
18

スレッドセーフなプール － Win32 API版
• CriticalSectionに関するAPI– リソースの排他制御（ロック）に使用• InitializeCriticalSection(Ex)• EnterCriticalSection• LeaveCriticalSection• DeleteCriticalSection• ConditionVariableに関するAPI– ビジーループや無駄なポーリングを回避するため– ただし、Vista / Windows 7 / Windows 2008 でのみ利用可• InitializeConditionVariable• SleepConditionVariableCS• WakeAllConditionVariable– WakeConditionVariable19テストコード － 複数スレッドの起動• _beginthreadex, WaitForSingleObject, CloseHandle

20テストコード － リソースプールへのアクセス• try … __finally を忘れずに!!

21

スレッドセーフなプール － POSIXスレッド版• OSに依存しないPOSIXスレッドを利用– Open Source POSIX Threads for Win32• http://sourceware.org/pthreads-win32/– デベロッパーキャンプで紹介済みです[gSOAP, Pthreads-Win32, OpenSSLを使ったSOAS C/Sアプリ開発]• http://edn.embarcadero.com/article/images/33870/devcamp03_a
1.pdf• 排他制御（ロック）に使用する関数– pthread_mutex_init, pthread_mutex_destroy– pthread_mutex_lock, pthread_mutex_unlock• 条件による待機に使用する関数– pthread_cond_init, pthread_cond_destroy– pthread_cond_timewait, pthread_cond_broadcast
22テストコード － 複数スレッドの起動• pthread_create, pthread_join

23

テストコード － リソースプールへのアクセス

24

スレッドセーフなプール － Boost::Thread版• OSに依存しないスレッドライブラリ– C++Builder XE には、Boost 1.39 が付属– Boost::ThreadライブラリのDLLが必要• リリース用: boost_thread-bcb-mt-1_39.dll• デバッグ用: boost_thread-bcb-mt-d-1_39.dll• 排他制御（ロック）のコードがキレイ– boost::unique_lockオブジェクトのスコープ• Lockableには、boost::recursive_mutex を使用• Lockable, Condition Variable の初期化コードが不要– boost::condition_variable_anyクラス• timed_waitメソッド• notify_allメソッド
25

テストコード － 複数スレッドの起動• boost::threadクラス, operator()(), joinメソッド

26

テストコード － リソースプールへのアクセス

27

17Th
Developer Camp

5

デモ
リソースプールの動作検証

28

17Th
Developer Camp

Q&A

29