1. カーネル空間およびユーザランドのソフトウェアの開発技術 |
---|
|
システムコールによるカーネルとユーザランドの連携 | |||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
カーネルとユーザプロセスの仮想メモリ空間 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
オペレーティングシステムの基本構造 |
---|
+--------------------------------------------------+ |+------------------------------------------------+| || ユーザランドプログラム || |+------------------------------------------------+| | ^ +------------------------+| ユーザプロセス空間 | | | ライブラリ || | | +------------------------+| +-------------|-----------------------^------------+ | | +-------------v-----------------------v------------+ |+------------------------------------------------+| || システムコールインタフェース || |+------------------------------------------------+| |+--------------------+ +------------------------+| || プロセス管理 | | ファイルシステム || |+--------------------+ | [バッファキャッシュ] || | +------------------------+| |+--------------------+ +------------------------+| カーネル空間 || メモリ管理 | | ネットワーク層 || |+--------------------+ +------------------------+| | | |+--------------------+ +------------------------+| || アーキテクチャ | | デバイスドライバ || || 依存コード | | || |+--------------------+ +------------------------+| +-----------^-------------------------^------------+ | | v v +--------------------------------------------------+ | ハードウェア | +--------------------------------------------------+ |
プログラム開発の視点からの比較 | |||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
カーネルソースコードの構成 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
デバイスドライバ |
---|
ハードウェアデバイスの存在を検出(probe)し、デバイス自身およびソフトウェア状態を初期化する。 システムコールインタフェース経由の入出力要求を処理し、必要に応じてカレントプロセスを中断させる(tsleep()を使用)。 システムが割り込みを受け取った時に呼び出される割り込みハンドラであり、中断させたプロセスを再開させる(wakeup()を使用)。 入力要求 出力要求 | | | +-------------------------------------+ | | |+------------+ +------------+| | | ||入力バッファ| |出力バッファ|<---+ | |+------------+ +------------+| Upper Half +-->| --+ ^ | | ----------|-------|----------------------|---------- | | +----------------+ | | | | |割り込みハンドラ| | | Bottm Half | | +----------------+ | | +---|-------^------------^---------|--+ v | | v +---------------------------------+ | ハードウェアデバイス | +---------------------------------+ |
カーネルのデバッグ |
---|
どこまで実行が進んだのか、あるいはどこで変数の内容が破壊されたのかを調べる原始的な方法。 突然リブートして出力内容を確認できない場合、調べたい個所に無限ループやhlt命令を埋め込む。 printf()が使えない局面(ブートストラップの初期段階やスタンバイ状態からの復帰など)では、 替わりにBEEP音を鳴らしてどこまで処理が進んでいるかを探索する。 机上デバッグの段階でC言語ソースのレベルでは気づきにくいバグも、アセンブリ言語ソースの レベルで調べると判明するバグも多い。 カーネルのビルドにおいて、"cc -c"となっているところを"cc -S"に変えてアセンブリ言語ソースを生成させる。 カーネルに内蔵されているアセンブリ言語レベルの簡易デバッガとしてFreeBSDにはDDBがある。 カーネル内部処理での例外発生時にリブートする前に起動される。また、コンソールから「Ctrl+Alt+Esc」を 入力することで、手動で起動可能。 FreeBSDにはシステムクラッシュ時に実メモリの内容を専用パーティションへ書き込み、リブート後に その内容をクラッシュダンプとしてファイルへ格納し、gdbによるソースレベルの解析をおこなう。 シリアルケーブル(NULLモデムケーブル)で接続されたPC側でgdbを起動してオンラインカーネルデバッグをおこなうことも可能。 |
カーネル組み込みデバッガ: DDB | ||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
ソースレベルカーネルデバッガ: KGDB |
---|
[root@freebsd ~]# cd /sys/i386/compile/VMWBSD [root@freebsd /sys/i386/compile/VMWBSD]# kgdb kernel.debug /var/crash/vmcore.0 [snip] panic: free: address 0xce747c00(0xce747000) has not been allocated. KDB: enter: panic Uptime: 2m48s Dumping 191 MB (3 chunks) chunk 0: 1MB (159 pages) ... ok chunk 1: 190MB (48624 pages) 174 158 142 126 110 94 78 62 46 30 14 ... ok chunk 2: 1MB (256 pages) #0 doadump () at pcpu.h:165 165 __asm __volatile("movl %%fs:0,%0" : "=r" (td)); (kgdb) bt #0 doadump () at pcpu.h:165 #1 0xc06797d6 in boot (howto=260) at ../../../kern/kern_shutdown.c:409 #2 0xc0679a9c in panic ( fmt=0xc0904e1a "free: address %p(%p) has not been allocated.\n") at ../../../kern/kern_shutdown.c:565 #3 0xc066ec46 in free (addr=0xce747c00, mtp=0xc0979ba0) at ../../../kern/kern_malloc.c:374 #4 0xc05c7172 in seq_write (dev=0xc1d82200, uio=0x64, flag=0) at ../../../dev/seq/seq.c:97 #5 0xc062e5ec in devfs_write_f (fp=0xc1ef1af8, uio=0xce747cbc, cred=0xc1d25d00, flags=0, td=0xc1e4d780) at ../../../fs/devfs/devfs_vnops.c:1290 #6 0xc069c50f in dofilewrite (td=0xc1e4d780, fd=1, fp=0xc1ef1af8, auio=0xce747cbc, offset=Unhandled dwarf expression opcode 0x93 ) at file.h:252 #7 0xc069c3b3 in kern_writev (td=0xc1e4d780, fd=1, auio=0xce747cbc) at ../../../kern/sys_generic.c:402 #8 0xc069c2d9 in write (td=0xc1e4d780, uap=0x0) at ../../../kern/sys_generic.c:326 #9 0xc08908f3 in syscall (frame= {tf_fs = 59, tf_es = 59, tf_ds = 59, tf_edi = 0, tf_esi = 134651904, tf_eb p = -1077941192, tf_isp = -831226524, tf_ebx = 4, tf_edx = 134651904, tf_ecx = 4 , tf_eax = 4, tf_trapno = 12, tf_err = 2, tf_eip = 672805679, tf_cs = 51, tf_efl ags = 66118, tf_esp = -1077941236, tf_ss = 59}) at ../../../i386/i386/trap.c:983 ---Type |
[演習] 擬似デバイスドライバ seq の開発とデバッグ |
---|
$ head -1 /dev/seq 0 $ head -1 /dev/seq 1 $ cat /dev/seq 2 3 4 (中略) 123456 ^C $ |
[演習] 擬似デバイスドライバ seq の開発とデバッグ(続き) |
---|
34 static struct sx sequence_lock; 35 static unsigned long sequence_number = 0; 36 static char sequence_buffer[DEV_BSIZE]; [snip] 47 /* ARGSUSED */ 48 static int 49 seq_read(struct cdev *dev __unused, struct uio *uio, int flag) 50 { 51 int c = 0, error; 52 void *seq_buf; 53 54 sx_xlock(&sequence_lock); 55 snprintf(sequence_buffer, sizeof(sequence_buffer), 56 "%lu\n", sequence_number); 57 seq_buf = (void *)sequence_buffer; 58 59 c = strlen(sequence_buffer); 60 error = uiomove(seq_buf, c, uio); 61 if (error == 0) 62 { 63 sequence_number++; 64 } 65 66 sx_xunlock(&sequence_lock); 67 return (error); 68 } # cd /sys/dev/seq # vi seq.c # cd /sys/i386/compile/VMWBSD # make # make install # shutdown -r now |
[演習] 擬似デバイスドライバ seq の開発とデバッグ(続き) |
---|
現在、シーケンス番号を生成する関数 seq_read() は問題なく動作しているが、シーケンス番号を設定する関数 seq_write() はシステムクラッシュ(panic)を引き起こす。 カーネル組み込みデバッガDDBおよびカーネルデバッガKGDBを使用して原因を調査し、デバイスドライバのソースコードを適切に修正せよ。 シーケンス番号の最大値は32ビットとなっているが、これを64ビットまで拡張したい。 この要望に合うようにデバイスドライバのソースコードを適切に修正せよ。 デバイスドライバ seq のソースコードを参考に、POSIX Epoch (1970年1月1日0:00(GMT))からの経過秒数を示す文字列を 取得するデバイスドライバ time を作成せよ。 カーネル内部の現在時刻の取得にはシステムコールインタフェース /sys/kern/kern_time.c:gettimeofday()、 経過秒数への変換はライブラリtime(3)のソース /usr/src/lib/libc/gen/time.c を参考にする。 なお、新規のデバイスドライバのソースコードの組み込みには、/sys/conf/files、/sys/i386/conf/VMWBSDにある seq 用の記述を参考にする。 また、新規デバイスを追加したので、カーネルの再構築とリビルドを以下の手順でおこなう必要がある。
# rm -rf /sys/i386/compile/VMWBSD # cd /sys/i386/config # vi VMWBSD ←device timeを追加 # config VMWBSD # cd ../compile/VMWBSD # make cleandepend; make depend # make # make install # shutdown -r now |
2. RDBMSとJavaによるWebアプリケーションの開発 |
---|
Webの機能を動的に拡張するために考案されたサーバサイド技術。プロセス生成・消滅のオーバヘッドや状態保持の問題。 Microsoftによって開発された技術で、HTMLにVBScriptを埋め込んだページをWebサーバのモジュールであるスクリプトエンジンによって実行する。 MS IISが前提環境。 クライアントサイドの技術で、SunMicrosystemsによって開発されたJava言語で記述されたアプリケーションを、 Webブラウザ上にダウンロードして実行させる技術。 生産性、マルチメディア(特に音声や動画)の扱いが劣ることから、競合技術であるFlashにシェアを奪われる。 クライアントサイドの技術で、Netscape 社が開発したスクリプト言語で、Webブラウザ側にあるインタプリタで実行される。 Java言語とは互換性がまったく無い。Ajaxの基盤技術の一つになっている。 JavaクラスをWebサーバへロードして、メソッドを呼び出すことで動的な応答をさせる技術。 Javaプログラムのメソッド内でHTMLを生成するため、ページのデザインが難しい。 ASPに対抗して開発された技術で、ASPと同様にHTMLにJavaコードを埋め込む。Servletの弱点を克服。 ロジックとデザインの分離を可能とし、さまざまなプラットフォームで稼動できる。 ASPやJSPで実現されたロジックとデザインの分離を、より明確にブロック分けしたMicrosoftによって開発された技術。 開発言語としてC#、VBなど複数のものが選択可能となり、どの言語も同一のライブラリである.NET Frameworkが使用できる。 JavaScriptを主軸とした複合技術によるサーバとの非同期通信というアプローチをとった技術で、ページ遷移を行わずにコンテンツの動的更新が可能となる。 |
Webアプリケーションの典型的構成 |
---|
+---------------+ +--------+ +--------+ +--------+ |Webクライアント|<-------->| | | | | | +---------------+ | | | | | | | | | | | | | | | | | | +---------------+ | Web | | AP | | DB | |Webクライアント|<-------->| サーバ |<->| サーバ |<->| サーバ | +---------------+ | | | | | | | | | | | | | | | | | | | | | | | | +---------------+ | | | | | | |Webクライアント|<-------->| | | | | | +---------------+ +--------+ +--------+ +--------+ WebクライアントからのHTTPによるアクセス要求を分散処理する。 HTTPトランザクションの一貫性を保持し、システム固有の処理を行い、バックエンドで動作するデータベースなどの検索/加工処理などを司る。 今回の演習ではTomcatを使用する。 システムのデータや管理情報を司る。今回の演習ではPostgreSQLを使用する。 |
[演習]会議室予約システムの仕様 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
Enter your user name and mail address Or create a new account (User ID = 2) List of rooms
|
[演習]会議室予約システムの仕様(続き) |
---|
(User ID = 2) Available rooms (User ID = 2) Confirmation |
[演習]会議室予約システムの構築準備: DB作成 |
---|
1 DROP SEQUENCE user_master_uids_seq; 2 3 CREATE SEQUENCE user_master_uids_seq 4 START WITH 1 5 INCREMENT BY 1 6 NO MAXVALUE 7 NO MINVALUE 8 CACHE 1; 9 10 DROP TABLE user_master; 11 12 CREATE TABLE user_master ( 13 uid INT 14 DEFAULT NEXTVAL('user_master_uids_seq'::text) NOT NULL, 15 user_name VARCHAR(128) 16 NOT NULL, 17 mail_address VARCHAR(128) 18 NOT NULL, 19 20 CONSTRAINT user_master_pk PRIMARY KEY ( 21 uid 22 ) 23 ); 24 25 INSERT INTO user_master(user_name, mail_address) 26 VALUES('iwasaki', 'iwasaki@freebsd.org'); 27 28 DROP TABLE room_master; 29 30 CREATE TABLE room_master ( 31 rid INT, 32 room_name VARCHAR(128), 33 capacity INT, 34 35 CONSTRAINT room_master_pk PRIMARY KEY ( 36 rid 37 ) 38 ); 39 40 INSERT INTO room_master(rid, room_name, capacity) VALUES(1, 'room#01', 10); 41 INSERT INTO room_master(rid, room_name, capacity) VALUES(2, 'room#02', 10); 42 INSERT INTO room_master(rid, room_name, capacity) VALUES(3, 'room#03', 20); 43 INSERT INTO room_master(rid, room_name, capacity) VALUES(4, 'room#04', 20); 44 INSERT INTO room_master(rid, room_name, capacity) VALUES(5, 'room#05', 30); 45 46 DROP TABLE room_reservation; 47 48 CREATE TABLE room_reservation ( 49 rid INT, 50 reserved_from TIMESTAMP, 51 reserved_to TIMESTAMP, 52 uid INT, 53 54 CONSTRAINT room_reservation_pk PRIMARY KEY ( 55 rid, 56 reserved_from, 57 reserved_to 58 ) 59 ); 60 61 INSERT INTO room_reservation(rid, reserved_from, reserved_to, uid) 62 VALUES(1, TO_TIMESTAMP('2007-10-01 08:00', 'yyyy-mm-dd hh24:mi'), 63 TO_TIMESTAMP('2007-10-01 12:00', 'yyyy-mm-dd hh24:mi'), 1); 64 INSERT INTO room_reservation(rid, reserved_from, reserved_to, uid) 65 VALUES(2, TO_TIMESTAMP('2007-10-01 07:00', 'yyyy-mm-dd hh24:mi'), 66 TO_TIMESTAMP('2007-10-03 20:00', 'yyyy-mm-dd hh24:mi'), 1); |
[演習]会議室予約システムの構築準備: DB作成(続き) |
---|
$ psql < create_db.sql DROP SEQUENCE CREATE SEQUENCE DROP TABLE NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "user_master_pk" for table "user_master" CREATE TABLE INSERT 413825 1 DROP TABLE NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "room_master_pk" for table "room_master" CREATE TABLE INSERT 413830 1 INSERT 413831 1 INSERT 413832 1 INSERT 413833 1 INSERT 413834 1 DROP TABLE NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "room_reservation_pk" for table "room_reservation" CREATE TABLE INSERT 413839 1 INSERT 413840 1 DROP FUNCTION CREATE FUNCTION is_occupied ------------- f (1 row) [snip] is_occupied ------------- f (1 row) $ |
[演習]会議室予約システムの構築準備: DB作成(続き2) |
---|
$ psql Welcome to psql 7.4.17, the PostgreSQL interactive terminal. Type: \copyright for distribution terms \h for help with SQL commands \? for help on internal slash commands \g or terminate with semicolon to execute query \q to quit iwasaki=> \? General \c[onnect] [DBNAME|- [USER]] connect to new database (currently "iwasaki") \cd [DIR] change the current working directory \copyright show PostgreSQL usage and distribution terms \encoding [ENCODING] show or set client encoding \h [NAME] help on syntax of SQL commands, * for all commands \q quit psql \set [NAME [VALUE]] set internal variable, or list all if no parameters \timing toggle timing of commands (currently off) \unset NAME unset (delete) internal variable \! [COMMAND] execute command in shell or start interactive shell Query Buffer \e [FILE] edit the query buffer (or file) with external editor \g [FILE] send query buffer to server (and results to file or |pipe) \p show the contents of the query buffer \r reset (clear) the query buffer \s [FILE] display history or save it to file \w [FILE] write query buffer to file Input/Output \echo [STRING] write string to standard output \i FILE execute commands from file \o [FILE] send all query results to file or |pipe \qecho [STRING] write string to query output stream (see \o) Informational \d [NAME] describe table, index, sequence, or view \d{t|i|s|v|S} [PATTERN] (add "+" for more detail) list tables/indexes/sequences/views/system tables \da [PATTERN] list aggregate functions \dc [PATTERN] list conversions \dC list casts \dd [PATTERN] show comment for object \dD [PATTERN] list domains \df [PATTERN] list functions (add "+" for more detail) \dn [PATTERN] list schemas \do [NAME] list operators \dl list large objects, same as \lo_list \dl list large objects, same as \lo_list \dp [PATTERN] list table access privileges \dT [PATTERN] list data types (add "+" for more detail) \du [PATTERN] list users \l list all databases (add "+" for more detail) \z [PATTERN] list table access privileges (same as \dp) Formatting \a toggle between unaligned and aligned output mode \C [STRING] set table title, or unset if none \f [STRING] show or set field separator for unaligned query output \H toggle HTML output mode (currently off) \pset NAME [VALUE] set table output option (NAME := {format|border|expanded|fieldsep|footer|null| recordsep|tuples_only|title|tableattr|pager}) \t show only rows (currently off) \T [STRING] set HTML <table> tag attributes, or unset if none \x toggle expanded output (currently off) Copy, Large Object \copy ... perform SQL COPY with data stream to the client host \lo_export \lo_import \lo_list \lo_unlink large object operations iwasaki=> \d List of relations Schema | Name | Type | Owner --------+----------------------+----------+--------- public | room_master | table | iwasaki public | room_reservation | table | iwasaki public | user_master | table | iwasaki public | user_master_uids_seq | sequence | iwasaki (4 rows) iwasaki=> select * from room_master; rid | room_name | capacity -----+-----------+---------- 1 | room#01 | 10 2 | room#02 | 10 3 | room#03 | 20 4 | room#04 | 20 5 | room#05 | 30 (5 rows) iwasaki=> select * from room_reservation; rid | reserved_from | reserved_to | uid -----+---------------------+---------------------+----- 1 | 2007-10-01 08:00:00 | 2007-10-01 12:00:00 | 1 2 | 2007-10-01 07:00:00 | 2007-10-03 20:00:00 | 1 (2 rows) iwasaki=> select * from user_master; uid | user_name | mail_address -----+-----------+--------------------- 1 | iwasaki | iwasaki@freebsd.org (1 row) iwasaki=> select r.*, u.user_name from room_reservation r, user_master u iwasaki-> where r.uid + = u.uid; rid | reserved_from | reserved_to | uid | user_name -----+---------------------+---------------------+-----+----------- 1 | 2007-10-01 08:00:00 | 2007-10-01 12:00:00 | 1 | iwasaki 2 | 2007-10-01 07:00:00 | 2007-10-03 20:00:00 | 1 | iwasaki (2 rows) iwasaki=> select m.*, r.uid from room_master m iwasaki-> left outer join iwasaki-> (select * from room_reservation iwasaki-> where (('2007-10-01 07:00' between reserved_from and reserved_to) or iwasaki-> ('2007-10-01 09:00' between reserved_from and reserved_to)) iwasaki-> ) r iwasaki-> on m.rid = r.rid; rid | room_name | capacity | uid -----+-----------+----------+----- 1 | room#01 | 10 | 1 2 | room#02 | 10 | 1 3 | room#03 | 20 | 4 | room#04 | 20 | 5 | room#05 | 30 | (5 rows) iwasaki=> begin; BEGIN iwasaki=> update room_reservation set uid = 2 where rid = 1; UPDATE 1 iwasaki=> select * from room_reservation; rid | reserved_from | reserved_to | uid -----+---------------------+---------------------+----- 1 | 2007-10-01 08:00:00 | 2007-10-01 12:00:00 | 2 2 | 2007-10-01 07:00:00 | 2007-10-03 20:00:00 | 1 (2 rows) iwasaki=> rollback; ROLLBACK iwasaki=> select * from room_reservation; rid | reserved_from | reserved_to | uid -----+---------------------+---------------------+----- 1 | 2007-10-01 08:00:00 | 2007-10-01 12:00:00 | 1 2 | 2007-10-01 07:00:00 | 2007-10-03 20:00:00 | 1 (2 rows) iwasaki=> \q $ |
[演習]会議室予約システムの構築準備: Javaクラスのコンパイル |
---|
$ cd course/day-2/roomReservation/WEB-INF/classes/ $ javac -classpath . simpleApp/*.java 1 package simpleApp; 2 3 import java.sql.*; 4 import javax.sql.*; 5 import javax.naming.*; 6 7 public class DataAccess 8 { 9 private DataSource ds_; 10 private Connection con_; 11 private Statement st_; 12 private ResultSet rs_; 13 14 public DataAccess() 15 { 16 ds_ = null; 17 con_ = null; 18 st_ = null; 19 rs_ = null; 20 } 21 22 public synchronized void open() 23 throws Exception 24 { 25 Context ctx = new InitialContext(); 26 ds_ = (DataSource)ctx.lookup("java:/comp/env/jdbc/postgres"); 27 con_ = ds_.getConnection(); 28 st_ = con_.createStatement(); 29 } 30 31 public ResultSet getResultSet(String sql) 32 throws Exception 33 { 34 if (st_.execute(sql)) { 35 rs_ = st_.getResultSet(); 36 return rs_; 37 } 38 return null; 39 } 40 41 public void execute(String sql) 42 throws Exception 43 { 44 st_.execute(sql); 45 } 46 47 public PreparedStatement prepareStatement(String sql) 48 throws Exception 49 { 50 return con_.prepareStatement(sql); 51 } 52 53 public synchronized void close() 54 throws Exception 55 { 56 if (rs_ != null ) { 57 rs_.close(); 58 } 59 if (st_ != null ) { 60 st_.close(); 61 } 62 if (con_ != null ) { 63 con_.close(); 64 } 65 } 66 } 67 |
[演習]会議室予約システムの構築準備: Javaクラスのコンパイル(続き) |
---|
1 package simpleApp; 2 3 import java.sql.*; 4 import simpleApp.*; 5 6 public class UserManager 7 { 8 private DataAccess da_; 9 10 public UserManager() 11 { 12 da_ = new DataAccess(); 13 } 14 15 public int search(String user_name, String mail_address) 16 throws Exception 17 { 18 int uid = -1; 19 20 try { 21 da_.open(); 22 23 String sql = "SELECT uid FROM user_master WHERE " + 24 "user_name=? AND mail_address=?"; 25 PreparedStatement ps = da_.prepareStatement(sql); 26 ps.setString(1, user_name); 27 ps.setString(2, mail_address); 28 ResultSet rs = ps.executeQuery(); 29 if (rs.next()) { 30 uid = rs.getInt("uid"); 31 } 32 rs.close(); 33 ps.close(); 34 } finally { 35 da_.close(); 36 } 37 38 return uid; 39 } 40 41 public int add(String user_name, String mail_address) 42 throws Exception 43 { 44 try { 45 da_.open(); 46 47 String sql = "INSERT INTO user_master(user_name, mail_address) " + 48 " VALUES(?, ?)"; 49 50 PreparedStatement ps = da_.prepareStatement(sql) 51 ps.setString(1, user_name); 52 ps.setString(2, mail_address); 53 ps.executeUpdate(); 54 55 ps.close(); 56 } finally { 57 da_.close(); 58 } 59 60 return search(user_name, mail_address); 61 } 62 } 63 |
[演習]会議室予約システムの構築 |
---|
会議室の検索結果に、予約希望期間にすでに予約が入っている会議室も含まれているため、
その会議室を予約処理するとダブルブッキングが生じてしまう。
会議室の検索結果の表示の際にすでに予約済みの会議室が選択できないよう、search.jspを修正せよ。
会議室の検索結果の表示の際にすでに予約済みの会議室があった場合、予約した人と連絡が取れるよう e-mailアドレスが表示されると便利である。この要望に合うよう、search.jspを修正せよ。 ミッション1でおこなった修正は一度会議室一覧の情報を作成してから、予約済み会議室を取得して会議室一覧の 情報を加工するものであった。この一連のDBへのアクセスは、実は一回のSELECTでおこなうことが可能である。 その方法を検討し、処理を効率的におこなうようsearch.jspを修正せよ。 ログイン後、自分が予約した会議室の予約情報を表示し、取り消し処理がおこなえるようにしたい。 search.jspを修正し、取り消し処理をおこなうcancel.jspを作成せよ。 なお、cancel.jspでは予約取り消し(Cancel Reservations)ボタンとsearch.jspへ戻る(Back)ボタンを用意し、どちらも search.jspへ遷移すること。 複数の会議室を一度に予約する場合、他のユーザの処理と競合してデータの矛盾が発生する可能性がある。 simpleApp.DataAccessクラスを拡張し、トランザクションの機能を追加せよ。 またreserve.jspをトランザクションを使用してデータの矛盾が起こらないように修正せよ。 具体的には、INSERT処理の前処理としてroom_masterの該当レコードをSELECT...FOR UPDATEでロック、 対象の会議室が予約希望期間に空いていることを最終確認し(空いていない場合はROLLBACKでトランザクションを終了させる)、 room_reservationへのINSERT処理をおこない、COMMITでトランザクションを確定させる。 処理の途中で例外が発生した場合は、catch()ブロックの中でROLLBACKをおこなうこと。 このシステムではJSPファイルでSQLを記述しDBアクセスをおこなっているが、これはデザインとロジックの分離の観点から好ましくない。 simleApp.UserManagerクラスを参考に、会議室予約業務ロジックを担当するクラスを設計および実装し、 これを使用するようすべてのJSPファイルを修正せよ。 このシステムには隠された不具合が多数存在する。それを発見し修正せよ。 |