Quantcast
Channel: Eclipse RCP, RAP Blog
Viewing all 71 articles
Browse latest View live

Spring MVC + Thymeleaf による WEB アプリケーション開発(4)一覧ビューの作成

$
0
0
今回は、前回作成した店舗エンティティ(Shop) のデータを一覧表示するビューを作成します。 一覧表示のために jQuery の dataTables プラグインを利用します。このプラグインを使えば、ページング機能や列ごとのソートなど、一般的に必要な機能を簡単に実現できます。(使い方についての詳細は次を参照してください:http://www.datatables.net

メニューの変更
まずは、このシリーズの1回目で作成したメニュー・フラグメント (frag01.html :: navbar)を修正して、店舗一覧を表示する機能(/shop/list)を追加します。shops テーブルにデータを1000件追加する機能(/shop/add1000)、全件削除する機能(/shop/deleteAll)も追加します。
WEB-INF/views/fragment/frag01.html (navbar フラグメント):
...
<!-- ナビゲーション・バー -->
<div th:fragment="navbar">
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" th:href="@{/}">Spring MVC デモ</a>
</div>

<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav navbar-right">
<li><a href="#">About</a></li>

<li class="dropdown"><a href="#"class="dropdown-toggle"
data-toggle="dropdown"> 店舗管理 <b class="caret"></b></a>
<ul class="dropdown-menu" role="menu">
<li><a th:href="@{/shop/list}">店舗一覧(ajax)</a></li>
<li class="divider"></li>
<li><a th:href="@{/shop/deleteAll}">全件削除</a></li>
<li><a th:href="@{/shop/add1000}">1000件追加</a></li>
</ul></li>
</ul>
</div>
</div>
</nav>
</div>
...
上記のコードでは、「店舗一覧」、「全件削除」、「1000件追加」の各メニューで @{...} 式を使って、リンク URL を設定します。<a>タグの th:href 属性の url 式 "@{/shop/list}"は href 属性で "/mvcdemo/shop/list"を指定するの同じです。

dataTables プラグインの追加
メニューと同じように HTMLヘッダ・フラグメント (frag01.html :: html)を修正して、dataTables プラグインを使用できるようにします(jquery.dataTables.css と jquery.dataTables.js を追加)。
WEB-INF/views/fragment/frag01.html (htmlhead フラグメント):
...
<!-- HTMLヘッダ -->
<head th:fragment="htmlhead">
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title th:text="${title}">(title)</title>

<!-- CSS -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" />
<link rel="stylesheet" href="//cdn.datatables.net/1.9.4/css/jquery.dataTables.css" />

<!-- HTML5 shim, IE6-8で HTML5 要素をサポートする -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->

<!-- JavaScript : jquery, bootstrap, custom -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="//cdn.datatables.net/1.9.4/js/jquery.dataTables.js"></script>
</head>
...

dataTables に JSON形式でデータを渡す
実際のアプリでは一度に何万件ものデータを取得して、何千ページもページを切り替えるような仕様はユーザーにとって使いにくいものです。そのため、検索やフィルタ処理で取得件数を数十件から多くても数百件に絞り込むことが考えられます。この程度の件数であればビューの表示時間は許容範囲内に抑えられます。このアプリでは、dataTables のデータ取得を ajax 対応にして、何千ページものページングを行う仕様の場合でも、実際に取得する件数は指定ページの1ページ分の行数だけ取得する事で、レスポンスを一定時間内に抑えるようにします。dataTables に1ページ分のデータを渡すオブジェクトを作成します。
com.itrane.mvcdemo.comand.DataTableObject:
publicclass DataTableObject<T> implements Serializable {

privatestaticfinallong serialVersionUID = 1L;

private List<T> aaData;
privateint sEcho;
private Long iTotalRecords;
private Integer iTotalDisplayRecords;

//getter , setter は省略
...
}
このオブジェクトは、データベースから取得した1ページ分の店舗データ aaData と、dataTables のページングを制御するための sEcho, iTotalRecords, iTotalDisplay の値を保持します。Spring MVC ではコントローラからビューへデータを様々な方法で送る事ができます。ここでは、上記オブジェクトを Json形式に変換してビューへ送ります。

コントローラの追加
ShopController クラスを新規作成して、店舗データ追加機能、店舗一覧を表示する機能を作成します。
com.itrane.mvcdemo.controller.ShopController:
@Controller
@RequestMapping("/shop")
publicclass ShopController {

finalstaticprivate Logger logger = LoggerFactory.getLogger(ShopController.class);

@Autowired
private ShopService shopService;

/**
* 店舗一覧ビューを表示する.
* @return ModelAndView: 表示ビューは shop_list.html
*/

@RequestMapping(value = "/list")
public ModelAndView shopList() {
logger.debug("");
ModelAndView mav = new ModelAndView("/shop/shop_list");
return mav;
}

/**
* 店舗一覧ビューの dataTables で指定ページのデータを取得する.
* @param HttpServletRequest: dataTables の表示ページ、表示件数、ソート列等を取得。
* @return String: DataTableObject<Shop> の JSon形式
*/

@RequestMapping(value = "/page", produces="text/html;charset=UTF-8")
public @ResponseBody String getShopPage(HttpServletRequest request) {
Map<String, String[]> params = request.getParameterMap();

String sortCol = params.get("mDataProp_"
+ getParam("iSortCol_0", params, 0))[0];
String sortDir = getParam("sSortDir_0", params, "");

int iDisplayLength = getParam("iDisplayLength", params, 10);
int pageNum = getParam("iDisplayStart", params, 0) / iDisplayLength;
logger.debug("sortCol="+sortCol+", sortDir=" + sortDir
+ ", page=" + pageNum + ", size=" + iDisplayLength);

List<Shop> shopList = shopService.findByPage(pageNum,
iDisplayLength, sortDir, new String[] {sortCol} );

long total = shopService.countShops();

DataTableObject<Shop> dt = new DataTableObject<Shop>();
dt.setAaData(shopList);
dt.setiTotalDisplayRecords((int)total);
dt.setiTotalRecords(total);
dt.setsEcho(getParam("sEcho", params, 0));
return toJson(dt);
}

privateint getParam(String key, Map<String, String[]> params, int def) {
String[] vals = params.get(key);
if (vals==null) {
return def;
} else {
return Integer.valueOf(vals[0]);
}
}
private String getParam(String key, Map<String, String[]> params, String def) {
String[] vals = params.get(key);
if (vals==null) {
return def;
} else {
return vals[0];
}
}

private String toJson(Object dt){
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(dt);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}

/**
* 店舗テーブルの全データを削除する.
* @return ModelAndView: 表示ビューは shop_list.html
*/

@RequestMapping(value="/deleteAll", method=RequestMethod.GET)
public ModelAndView delAll() {
logger.debug("");
ModelAndView mav = new ModelAndView("/shop/shop_list");
shopService.deleteAll();
return mav;
}

/**
* 店舗テーブルに1000件のデータを追加する.
* @return ModelAndView: 表示ビューは shop_list.html
*/

@RequestMapping(value="/add1000", method=RequestMethod.GET)
public ModelAndView add1000() {
logger.debug("");
ModelAndView mav = new ModelAndView("/shop/shop_list");

//1000件追加処理
long cnt = shopService.countShops();
List<Shop> shops = new ArrayList<Shop>(1000);
for (long i=cnt+1; i<=cnt+1000; i++) {
Shop s = new Shop();
s.setName("店舗" + (10000000 + i));
s.setPhone("03" + (10000000 + i));
s.setEmail("tenpo" + (10000000 +i) + "@mvc.com");
s.setKaitenBi("2014/01/01");
int n = (int)((i - cnt) % 80) + 1;
s.setEmplNumber(10+80%n);
shops.add(s);
}
shopService.create(shops);
return mav;
}
}
上記の各メソッドについて、簡単に説明します。
  • shopList( ) メソッド:
     店舗一覧ビュー "/shop/shop_list.htm"を表示します。
  • page( ) メソッド:
    店舗一覧ビューで、dataTables ページング制御の各ボタンが押されたときに呼び出される。押されたボタンに応じたリクエストパラメータが渡されるのでそれを解析して、データベースから該当ページのデータを取得します。
    List<Shop> shopList = shopService.findByPage(pageNum, 
    iDisplayLength, sortDir, new String[] {sortCol} );
    取得したデータと dataTables の制御用データを、DataTableObject インスタンスに設定し、toJson( ) メソッドで Json 形式のデータに変換して、ビューに送ります。
  • deleteAll( ) メソッド:
    店舗テーブル(shops)の全データを削除します。
    shopService.deleteAll();
  • add1000( ) メソッド:
    店舗テーブルに自動生成したデータ1000件を追加します。
    long cnt = shopService.countShops();
    List<Shop> shops = new ArrayList<Shop>(1000);
    for (long i=cnt+1; i<=cnt+1000; i++) {
    Shop s = new Shop();
    ...
    shops.add(s);
    }
    shopService.create(shops);
データの取得、削除、追加は、ShopServiceの findByPage( ), deleteAll( ), create( ) メソッドを使用して行います。

ビューの作成
ShopController の listShop( )  メソッドの実行の結果表示されるビューを作成します。
WEB-INF/views/shop/list_shop.html:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head th:include="fragment/frag01 :: htmlhead"
th:with="title='Spring MVC デモ'"></head>

<style>
.contents { margin-top: 30px; }
.table-container{ width:1100px; }
.btn_edit{ font-size: 14px; }
.btn_delete{ font-size: 14px; }
.alignRight { text-align: right; }
</style>
<script>
<!--
$(document).ready(function () {
var oTable = $("#shops").dataTable({
"bProcessing": true,
"bServerSide": true,
"bLengthChange": true,
"sAjaxSource": "/mvcdemo/shop/page.html",
"aLengthMenu": [[10, 20,],[10, 20]],
"aoColumns": [
{"sTitle": "ID", "mData": "id", "sWidth": 40, "sClass": "center" },
{"sTitle": "店舗名", "mData": "name", "sWidth": 200 },
{"sTitle": "電話番号", "mData": "phone", "sWidth": 70 },
{"sTitle": "メール", "mData": "email", "sWidth": 240 },
{"sTitle": "開店日", "mData": "kaitenBi", "sWidth": 60 },
{"sTitle": "従業員数", "mData": "emplNumber", "sWidth": 100, "sClass": "alignRight",
"fnCreatedCell": function (td, cellData, rowData, row, col) {
if ( cellData > 15 ) {
$(td).css('color', 'red');
}
}
},
],
"fnServerData": function(sSource, aoData, fnCallback) {
$.ajax({
"dataType" : 'json',
"contentType" : "application/json;charset=UTF-8",
"type" : "GET",
"url" : sSource,
"data" : aoData,
"success" : fnCallback
});
},
"oLanguage": {
"sLengthMenu": "表示 _MENU_",
"sSearch": "検索:",
"oPaginate": {
"sFirst": "先頭 ",
"sLast": "最後",
"sNext": "次 ",
"sPrevious": "前"
},
"sInfo": "_START_件 ~ _END_件 / _TOTAL_件"
},
"sPaginationType" : "full_numbers"
}); //dataTables
});
-->
</script>

<body>

<!-- $$$ Navigation Bar $$$$$ -->
<div th:include="fragment/frag01 :: navbar"></div>

<div class="contents">
<div class="container">

<!-- $$$ Contents Header $$$$$ -->
<div class="row">
<div class="col-lg-12">
<h1 class="page-header"> 店舗一覧
<small>(ajaxバージョン)</small>
</h1>
</div>
</div>

<!-- $$$ Contents Body $$$$$ -->
<div class="row">
<div class="col-lg-12">
<div class="table-container">
<table id="shops"class="display">
<thead>
<tr>
<th>id</th>
<th>店舗名</th>
<th>電話番号</th>
<th>メール</th>
<th>開店日</th>
<th>従業員数</th>
</tr>
</thead>
<tbody>
</tbody>
</table&gt
<br /><br />
</div>

<!-- $$$ Contents Footer $$$$$ -->
<div th:include="fragment/frag01 :: footer"></div>
</div>
</div>

</div> <!-- Container END -->
</div> <!-- Contents END -->

</body>
</html>
上記のコードのは javascript のパートと HTML パートで構成されています。

  • HTML のパート:重要なのは <table>..</table> の部分です。 dataTables プラグインによって、<tbody>..</tbody> の部分が取得データで置き換えられます。
  • javascript のパート:テーブルの表示形式や、データの取得方法を指定します。
    主な部分について説明します

実行結果
アプリケーション起動後、店舗データを1000件追加して、店舗一覧の10ページを表示した結果は次のようになります:

まとめ
データの一覧を表示して、選択行に対して様々な操作を行うのは、アプリケーションの一般的な機能です。dataTables プラグインを使用する事で、データ一覧機能として要求される様々な機能や外観を簡単に実現できます。アプリによっては特別な要件を満たす統一したデザインのテーブル機能を独自に開発する方が簡単ですが、多くの場合は dataTables をカスタマイズして簡単に対応できます。

ソースコード
このコードは GitHub-branch:mvcdemo04 からダウンロードできます。


Spring MVC + Thymeleaf による WEB アプリケーション開発(5)一覧ビューの変更

$
0
0
今回は前回作成した一覧ビューを変更して、テーブルの各行に編集ボタン、削除ボタンを追加します。テーブルの下に店舗追加ボタンも追加します。その他にもいくつかの変更を行います。

バージョンの変更
HTMLヘッダ・フラグメントの (frag01.html) を修正して、jQuery, dataTables のバージョンをそれぞれ、以下のように変更します。
WEB-INF/views/frag01.html (htmlhead フラグメント):
...
<!-- HTMLヘッダ -->
<head th:fragment="htmlhead">
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title th:text="${title}">(title)</title>

<!-- CSS -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" />
<link rel="stylesheet" href="//cdn.datatables.net/1.10.1/css/jquery.dataTables.css" />

<!-- HTML5 shim, IE6-8で HTML5 要素をサポートする -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->

<!-- JavaScript : jquery, bootstrap, custom -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="//cdn.datatables.net/1.10.1/js/jquery.dataTables.js"></script>
</head>
...



編集ボタンと削除ボタンの追加
店舗一覧テーブルの各行に編集、削除のためのボタンを追加します。shop_list.htmlの HTMLパートを以下のように変更して、ボタンを表示する"操作"列を追加します。
WEB-INF/views/shop/shop_list.html (HTMLパート):
   ... 
<table id="shops"class="display">
<thead>
<tr>
<th>id</th>
<th>店舗名</th>
<th>電話番号</th>
<th>メール</th>
<th>開店日</th>
<th>従業員数</th>
<th>操作</th>
</tr>
</thead>
...

さらに、javascript パートを以下のように変更します。
WEB-INF/views/shop/shop_list.html (javascript パート):
...
"aoColumns": [
{"sTitle": "ID", "mData": "id", "sWidth": 40, "sClass": "center" },
{"sTitle": "店舗名", "mData": "name", "sWidth": 200 },
{"sTitle": "電話番号", "mData": "phone", "sWidth": 70 },
{"sTitle": "メール", "mData": "email", "sWidth": 240 },
{"sTitle": "開店日", "mData": "kaitenBi", "sWidth": 60 },
{"sTitle": "従業員数", "mData": "emplNumber", "sWidth": 100, "sClass": "alignRight",
"fnCreatedCell": function (td, cellData, rowData, row, col) {
if ( cellData > 15 ) {
$(td).css('color', 'red');
}
}
},
{
"mData": "id", "sClass": "center", "sWidth": 100, "bSortable": false,
"fnCreatedCell": function (td, cellData, rowData, row, col) {
var bd = $('<button class="btn_delete">削除</button>');
bd.button();
bd.on('click',function(){
fBtn = true;
alert("Delete:"+cellData);
});
var be = $('<button class="btn_edit">編集</button>');
be.button();
be.on('click',function(){
fBtn = true;
alert("Edit:"+cellData);
});
$(td).empty();
$(td).prepend(bd);
$(td).prepend(be);
}
}
],
...
上記のコードでは列定義の "aoColumns"に"操作"列の定義を追加しています。列生成関数 "fnCreatedCell"で、"削除"ボタンと"編集"ボタンを生成し、ボタンのクリック時の関数を定義します。列生成関数の引数 cellData で該当行の店舗データの id が渡されます。現時点では、各ボタンのクリック時には alert でクリックされた行の id を表示します。

複数行選択機能の追加
表示ページの複数行を選択できるようにします。
WEB-INF/views/shop/shop_list.html (javascript パート):
    ...
$('#shops tbody').on( 'click', 'tr', function () {
if (fBtn==false) {
$(this).toggleClass('selected');
} else {
fBtn = false;
}
} );
...
行がクリックされたときに、CSS のクラス名 'selected'を追加(または削除)します。fBtn 変数は、ボタンが押された場合を除外するために使用しています。

店舗追加ボタンの追加
テーブルの下に"店舗追加"ボタン(BootStrap版)を追加します。
WEB-INF/views/shop/shop_list.html (HTMLパート):
  ...
<div class="table-container">
<table id="shops"class="display">
...
</table>
<br /><br />
</div>

<button id="shop-create"class="btn btn-primary btn-lg">店舗追加</button>
<!-- $$$ Contents Footer $$$$$ -->
<div th:include="fragment/frag01 :: footer"></div>
...


変更結果の確認
デモアプリケーションを実行して、変更の結果を確認します。
(ボタン追加とバージョン変更の確認)
メニューバーで、店舗管理 > 店舗一覧を選択します:
"操作"列が追加され、各行に"編集"ボタンと"削除"ボタンが追加され、またテーブルの下方には"店舗追加"ボタンが追加されます。

(複数行選択の確認)
上記画面で、3ページ目へ移動して、dataTables のページング機能を確認します。任意の行をいくつかクリックして選択します:

(ボタン押下時の動作を確認)
2行目 (id=2022) の編集ボタンをクリッックします:


次回は”店舗追加”ボタン、”編集”ボタンが押された時、入力フォームを表示して、店舗データの追加、修正が行えるようにします。

ソースコード
ソースコードは branch:mvcdemo05Aからダウンロードできます。

Spring MVC + Thymeleaf による WEB アプリケーション開発(6)新規、編集フォームを追加する

$
0
0
前回は、店舗一覧ビューを修正して、各行に"編集"および "削除"ボタンを、一覧テーブルの下部には "店舗追加"ボタンを追加しました。今回はこれらのボタンが押されたときに、店舗の追加/編集を行うためのフォームを表示できるようにします。

ダイアログフラグメントの作成
WEB-INF/views/fragment/frag01.htmlに Bootstrap の Modal を利用して、店舗新規追加フォーム、選択店舗編集フォームを持つダイアログのフラグメントを追加します。
WEB-INF/views/fragment/frag01.html ::  shop_create_modal:
...
<!-- Shop Create Modal -->
<div th:fragment="shop-create-modal">
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button"class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3 class="modal-title" id="myModalLabel">タイトル</h3>
</div>
<div class="modal-body">

<form class="form-horizontal" role="form" action="#" th:action="@{/mvc/edit}"
th:object="${shop}" method="post">
<fieldset>
<div class="form-group">
<label class="col-sm-2 control-label"for="name">店舗名</label>
<div class="col-sm-10 controls">
<input type="text" th:field="*{name}" placeholder="店舗名"
th:errorclass="fieldError" maxlength="20"class="form-control" />
<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}">不正な入力</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"for="phone">電話番号</label>
<div class="col-sm-10 controls">
<input type="text" th:field="*{phone}" placeholder="電話番号"
th:errorclass="fieldError" maxlength="13"class="form-control" />
<span th:if="${#fields.hasErrors('phone')}" th:errors="*{phone}">不正な入力</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"for="email">メール</label>
<div class="col-sm-10 controls">
<input type="text" th:field="*{email}" placeholder="メールアドレス"
th:errorclass="fieldError" maxlength="50"class="form-control" />
<span th:if="${#fields.hasErrors('email')}" th:errors="*{email}">不正な入力</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"for="kaitenBi">開店日</label>
<div class="col-sm-10 controls">
<input type="text" th:field="*{kaitenBi}"
th:errorclass="fieldError" maxlength="13"class="form-control" />
<span th:if="${#fields.hasErrors('kaitenBi')}" th:errors="*{kaitenBi}">不正な入力</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"for="emplNumber">従業員数</label>
<div class="col-sm-10 controls">
<input type="text" th:field="*{emplNumber}"
th:errorclass="fieldError" maxlength="3"class="form-control" />
<span th:if="${#fields.hasErrors('emplNumber')}" th:errors="*{emplNumber}">不正な入力</span>
</div>
</div>
</fieldset>
</form>

</div>
<div class="modal-footer">
<button type="button"class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button"class="btn btn-primary">保存</button>
</div>
</div>
</div>
</div>
</div>
...

上記のコードで、フォームのスタイルを決定する様々な CSS クラスは、Bootstrap の CSS (例:"modal-dialog", "form-horizontal", "form-group" など)を使用しています。<form> タグでは、SpringMVC と連携するために、Thymeleaf の属性(例:th:object, th:action など)を使用します。th:object 属性は、フォーム・バッキングビーンとして、ユーザー入力を受け取るために Shop オブジェクトを使用することを指定します。<input> タグの th:field 属性は Shop オブジェクトのフィールドを *{...} 式(例: *{name}, *{phone} など)で指定します。

コントローラー(ShopController)の修正
ShopContoller#shopList() メソッドを修正して、shop_list.html(店舗一覧ビュー)で Shop オブジェクト(モデル)を参照できるよう、ModelAndView に追加します。さらに店舗一覧ビューで"削除"ボタンが押された場合に呼び出されるメソッド deleteShop() と "編集"ボタンが押された場合に呼ばれるメソッド editShop() も追加します。
修正された com.itrane.controller.ShopController :
    ...
/**
* 店舗一覧ビューを表示する.
* @return ModelAndView: 表示ビューは shop_list.html
*/

@RequestMapping(value = "/list")
public ModelAndView shopList() {
logger.debug("");
ModelAndView mav = new ModelAndView("/shop/shop_list");
Shop shop = new Shop();
mav.addObject("shop", shop);
return mav;
}

...
@RequestMapping(value="/delete/{id}",
produces="text/html;charset=UTF-8", method=RequestMethod.GET)
public @ResponseBody String deleteShop(@PathVariable Integer id) {

logger.debug("id=" + id);
ModelAndView mav = new ModelAndView("redirect:/shop/list.html");
//Shop shop = shopService.delete(id);
String message = "店舗 を削除しました.";
return toJson(message);
}

@RequestMapping(value = "/edit/{id}",
produces="text/html;charset=UTF-8", method=RequestMethod.GET)
public @ResponseBody String editShop(@PathVariable Integer id) {
Shop shop = shopService.findById(id);
logger.debug("id=" + id + " shop.id=" + shop.getId() + " name=" + shop.getName());
return toJson(shop);
}
...


店舗一覧ビュー(shop_list.html)の javascript パートの修正
前回、ボタンを追加するために修正した javascript パートを次のように修正します。
WEB-INF/views/shop/shop_list.html(javascript パート):
(編集ボタンが押された場合の処理)
    ... 
var be = $('<button class="btn_edit">編集</button>');
be.button();
be.on('click',function(){
fBtn = true;
//ajax で get して成功時はダイアログを表示
$.getJSON("/mvcdemo/shop/edit/" + cellData + ".html", {},
function(shop){
$("#myModalLabel").html("店舗 (id="+cellData+") を修正してください");
setShop(shop.name, shop.phone, shop.email,
shop.kaitenBi, shop.emplNumber);
$('#myModal').modal('show');
});
});
...
jQuery の getJSON で ShopController#editShop( ) (リクエスト:"mvcdemo/shop/edit/{id}")を呼び出します。リクエストが成功した場合は、editShop( ) から、選択した id に対応する店舗データが JSON 形式で返されます(成功時に実行する関数に shop 引数として渡されます)。 この shop データを setShop(...) 関数でフォームの各フィールドにセットして、ダイアログを表示します。ダイアログおよびフォームは、上述のフラグメント(frg01.html :: shop-create-modal)を使用します。
(店舗追加ボタンが押された場合の処理)
    ...
$("#shop-create").click(function () {
$("#myModalLabel").html("新規店舗を追加してください。");
setShop("", "", "", "", "");
$('#myModal').modal('show');
});
...
編集の場合と同様に、上述のフラグメント(frag01.html :: shop-create-modal)を使用します。setShop(...) 関数でフォームの各フィールドをクリアした後、ダイアログを表示します。
上の2つの処理で呼んでいる setShop( ) 関数も追加します。
    ...
function setShop(name, phone, email, kaitenBi, emplNumber) {
$("#name").val(name);
$("#phone").val(phone);
$("#email").val(email);
$("#kaitenBi").val(kaitenBi);
$("#emplNumber").val(emplNumber);
}
...


店舗一覧ビュー(shop_list.html)の HTML パートの修正
最後にダイアログのフラグメント(frag01.html: shop-create-modal) を shop_list.html にインクルードするように、shop_list.html の HTML パートを次のように修正します。
WEB-INF/views/shop/shop_list.html:
...
</div> <!-- Container END -->
</div> <!-- Contents END -->

<div th:include="fragment/frag01 :: shop-create-modal"></div>

</body>
</html>

修正結果の確認
(店舗追加ボタンを押した場合)

(2ページ目の3行目の編集ボタンを押した場合)


追加フォーム、編集フォームは同じフラグメントを共用しています。このフォームでは保存ボタンが押された時、追加、編集のどちらのモードかを判定して、モード別にコントローラの追加メソッド、更新メソッドを呼び出してデータを登録/更新して、一覧画面に戻る処理を追加する必要があります。登録/更新処理は ShopService のメソッドを使えば簡単に実装できますが、一覧画面のどのページに戻すかはアプリの要件によります。このデモアプリでは保存ボタン押下後の処理は実装していません。この処理は次回以降のシリーズ(実用アプリの作成)で紹介したいと思います。

まとめ
今回のシリーズでは、SpringMVC と Tymeleaf, jQuery, Bootstrap などのフレームワークを使ってWEBアプリを作成しました。そして次のテーマを紹介しました。
  1. 各フレームワークを使用するための雛形となるプロジェクトの作成
  2. データベースアクセスのための設定
  3. モデルの作成
  4. 一覧ビューの作成と改良
  5. 編集フォームの作成
次回シリーズでは、同じフレームワークを利用して少し実用的なアプリを作成したいと思います。実用的なアプリでは次のテーマを扱いたいと思っています。
  1. JPA実装に EclipseLink を使った場合の pom.xml の作成
  2. JPA実装に EclipseLink を使った場合のデータベース設定
  3. enum 型や1:多関連のエンティティの作成
  4. 実用アプリでの一覧ビューやフォームの作成
  5. 入力データの検証
  6. エンドユーザーコンピューティング: CSV, エクセル形式でのデータのダウンロード
  7. 入力データを元にしたレポート(PDFやグラフ)の作成
  8. モバイル端末からのデータアクセス
  9. モバイル端末への通知

ソースコード
ソースは branch:mvcdemo06からダウンロードできます。


続還暦プログラマーの闘病記

$
0
0
このブログのテーマとは異なりますが、以前の投稿で間質性肺炎について書きました。

間質性肺炎
この病気は厄介な病気で、完治するまでにかなりの時間がかかります。再発して重篤な状態になることもあります。私の場合、燃え広がった炎症を大量のステロイドで鎮火し、ステロイドを徐々に減らしながらくすぶっている火を消して行きました。さらに、炎症部位がきれいになり、正常な呼吸が可能になったので、退院する事ができました。退院後は普通の生活が送れるまでに回復しましたが、まだ肺には火元の部分が残っていて、その中で火種がくすぶり続けている可能性があります。そのため退院後も少量のステロイドを服用し続ける必要があります。以前の投稿でお話ししたようにステロイドには様々な副作用があります。その副作用を抑えるための薬も同時に飲む必要があり、これらの薬にもまた別の副作用があるのです。間質性肺炎の治療と副作用の防止をバランスよく続けて行く事が必要と成ります。

再燃
時間が経つと少量のステロイドでは効き目が悪くなり再燃することがあります。残念ながら私もまた炎症が広がり始めてしまいました。そこで再入院して、ステロイドの量を急激に増やし、再度鎮火、ステロイドの減少という治療を行い、肺の回復は順調に行ったのですが、この過程で全てが悪い方向へと向かいました。

肝機能障害
今まで保たれていた治療と副作用の防止のバランスが一気に崩れて、著しい肝機能の低下が見られました。肝機能の低下は改善したわけではありませんが、横ばいになったので、退院して経過を見る事に成りました。

再入院
退院後も肝機能の低下は悪くなる一方で、かなり深刻な状態だと告げられました。そのため、再度入院して治療することします。

再開していたブログの投稿はしばらくお休みします。


三度の入院で学んだこと

$
0
0
今回もこのブログのテーマとは異なりますが、参考になることがあればと思い投稿します。

長い間 SE としてやってきて、顧客の問題を解決する仕事も数多くやってきました。同じように医療は患者の命と健康の問題を解決する仕事です。 問題の対象や解決手段に違いはあっても、分析と解決のアプローチは似ている点があります。

最初の入院時はそんなことを考える余裕も無く医療機関に任せきりでした。

2度目は肉体的にも余裕があり病院のシステムやスタッフを観察する余裕ができたので、測定されたバイタルや血液検査の結果などをデータとして保存してグラフとして経過を確認できるようにしました。この治療中に重い肝機能障害を起こし一度は退院しましたが、結局3度目の入院が必要になりました。

3度目の今回はSEとしての経験を活かして自身の病気と向き合うことにしました。できる限りのデータを収集・解析し、ネットで薬・病気・治療内容を調査して様々な比較も行いました。 2度目の入院時のデータも合わせて検査結果の値をグラフにしました。

これ以外にも肝臓の詳細な状態を示す様々な検査を行いましたが、それらのグラフも上図と同じようなパターンを示しています。この図から分かる事は2度目の入院の治療中に正常範囲だった肝機能の値が急激に悪化したことです(グラフのAの時点)。間質性肺炎の再燃治療による薬の副作用によることは明らかでしたが、原因と思われる薬を減らすなどの処置を行い経過をみることになりました(Bの時点)。入院して治療を始めたのはCの時点です。結果として入院後の治療が非常に困難なものになり、別の臓器に障害を起こすリスクも高まりました。

Bの時点で、第三者の意見なども聞いてもっと早期の治療を始めていればと、積極的に関与しなかったことを後悔しています。もちろん、ほとんどの人は医療の専門知識がなく、病院内で検査結果や検索データの分析をできるわけでもありません。また、救急患者の場合や重い病気で身動きがままならない患者にはあてはまりません。

いざ病気となったら聞きたい事が中々聞けない上、何を聞くべきも分かりません。健康な人の場合には難しいことですが、日頃から聞きたいことや確認しておくべき事などをまとめておくことが大切だと思いました。担当の医師に問題(病気の内容)と解決方法(治療内容・方針)を良く聞くことが大切です。問題点ばかりで、明快な解決策が示されない場合や、自分が疑問に思う治療については、第三者に相談して結論を出すことが必要です。そのためには、相談できるかかりつけの医師を持つことや地域の医療体制を把握しておくことが大切です。

難しい治療になるほど非常に高額な医療費がかかります。退院後も様々な支援が必要になる場合があります。保険制度や地域の支援体制についても調べておくことが重要です。こういった様々なことを日頃から調べていると、聞きたいことや自分が積極的に治療に関与していく気持ちも生まれてくると思います。


三度の入院を通して多くの事を考えることができました。肉体的にはまたリハビリから始めなければ成らないぐらいに衰弱しましたが、とりあえずブログを再開できるまでに回復できた事に感謝しています。

(1)SpringMVC+Thymelef+Bootstrap で実用アプリの開発: eclipselink を使用するDB設定

$
0
0
今回のシリーズでは、前回シリーズと同じフレームワークを利用して少し実用的なアプリを作成したいと思います。ただし今回は、JPA 実装として eclipelink を使った場合の pom.xml とデータベース設定について説明します。

プロジェクトの作成
アプリのプロジェクト名は healthcare とします。作成の仕方は前回シリーズの(1)と基本的に変わりません。

依存ライブラリの設定
以下は、properties と dependencies の部分です:
主な、前回シリーズとの違いは以下の通りです。
  1. フレームワークのバージョンアップ
    Spring フレームワークのバージョンを 3.x から 4.x に変更します。
    それにともない、Thymeleaf と Spring MVC を統合する thymeleaf-spring3 を thymeleaf-sprin4 に変更します。
  2. JPA 実装に eclipselink を使用します。
  3. 今回作成するアプリケーションの機能に必要な依存定義を追加します。

データベース設定
次に eclipselink を使用するために com.itrane.healthcare.init.DbConfig を次のように変更します:

注意:設定プロパティの例を示すために、様々なプロパティを設定していますが、アプリの規模や要件によって必要なものや最適値が決まります。

また DbConfig が参照する /src/resources/app.properties も次のように変更します:
これで、JPA実装に EclipsLink を使う pom.xml とデータベースの設定は終了です。

出力文字列の外部化
前回シリーズでは、Spring MVCのメッセージソースの設定を行いましたが、Thymeleaf の文字列・外部化の機能を使う例を示しました(例:home フォルダに home.html と home_ja.properties を置く)。ここでは Thymeleaf で Spring MVC で設定したメッセージソースを使えるように変更します。
まずメッセージゾルバー(messageResolver)を登録して、Thymeleaf のテンプレートエンジンの設定で、エンジンにリゾルバーを設定します。これにより、Thymleaf で Spring MVC のメッセージソースを使用できます。

app.properties で message.source.basename=WEB-INF/messages/messages を設定しているので、WEB-INF/messages フォルダに、次のような messages.properties を作成します。



ビューとコントローラの作成
起動時に表示されるビュー(home.html)と対応するコントローラ(HomeController.java)を作成します。
/WEB-INF/views/home/home.html :
アプリケーションを起動して、プロジェクトが正しく構成されていることを確認します:

ソースコード
ソースコードは GitHub からダウンロードしてください: branch1

(2)SpringMVC+Thymelef+Bootstrap で実用アプリの開発: アプリケーションの機能、モデル作成

$
0
0
健康管理アプリケーション(healthcare)全体は、カレンダー(定期検診、通院、ヘルパーの予約などの予定を管理)、お薬手帳(個人カルテ、処方箋などの管理)、バイタル管理(日々のバイタルを管理)などの機能が考えられます。このブログでは、デモアプリとして、その一部のバイタル管理の実装を説明します。

アプリケーションの機能(バイタル管理)
患者(健康な人の場合も)のバイタル(血圧、体温、体重、血糖値など)を管理します。
  • バイタルデータの保存:毎日、定期的にバイタルを測定して入力/保存します。
  • バイタルの一覧表示:システム日から遡って1週間ごとの測定したバイタルの一覧を表示します。
  • チャートの表示:一覧表示された測定値をグラフで表示します。
  • データのエキスポート:Excellファイル や PDF として登録データを出力します。
  • データのインポート:Excell ファイルからデータを読み込みます。
  • 薬管理と組み合わせた場合、モバイル端末への通知行うようにします。

モデルの作成
バイタルの測定値を管理するための Vital エンティティは次のようなものです:
バイタルマスターに設定した、順序、予定時間で、特定のタイプのバイタルを測定し、測定値と実際に記入された時間を保存します(注意:デモアプリでは実際の入力時間を設定するとデータ作成が面倒なため、予定時間をそのまま測定時間として設定することにします)。

また、これらの Vital の1日分を保持する Vod エンティティは次のようなものです:
Vital のタイプや測定時間は患者ごとに異なります。本来はユーザーごとにマスターを登録するべきですが、デモアプリでは簡単にするために1ユーザーのマスターのみ登録しています。
特定の患者(ユーザー)に対して、測定するバイタルタイプと測定時間を指定するマスター :
決められた予定時間に、特定のタイプ(血糖値、血圧など)のバイタルを測定します。同じタイプでも異なる時間に測定するた、め入力する人がわかりやすい名前(空腹時血糖など)をつけます。また、それぞれの患者(ユーザー)によって、注意すべき基準値が異なるため、それらも設定できるようにします。
例えば、マスターには次のようなデータを設定します:


リポジトリの作成
上記の各エンティの CRUD 操作のためのリポジトリを作成します。
com.itrane.healthcare.repo.VodRepository :
その他のリポジトリ: GitHub のソースコードを参照してください。

サービスの作成
上述したアプリケーションの機能を実現するためのサービスを作成します。
com.trane.healthcare.service.VodServiceImpl :
その他のサービス: GitHub のソースコードを参照してください。

マスターの初期化とダミーデータの作成
このアプリケーションでは、マスター保守などの機能は実装していません。また上述した機能(一覧表示、チャート表示、エクスポートなど)を確認する都合上、アプリケーション起動時にバイタルマスターにマスターデータを登録し、1ヶ月分の Vod データを登録しておきます。ここでは、アプリケーション・コンテキストの作成後の初期化処理でこれを行います。
com.trane.healthcare.init.WebAppConfig.java に次のコードを追加します:
これにより、InitMaster クラスの @PostConstruct 注釈付きメソッドの initData() が実行されてバイタルマスターテーブルにデータが登録されます。また CreateDummyData クラスの @PostConstruct 注釈付きメソッドの createData() が実行されてバイタルテーブルに1ヶ月分のデータが登録されます。
Vod に登録されたデータの一部:

Vital に登録されたデータの一部:

エンティティの関連
アプリケーションを起動して、MySQLWorkBench のリバースエンジニアリングの機能を使って、エンティティ間の関連が設計通りであることを確認できます。


まとめ
eclipslink を使ったデータベース設定、上述のモデルと対応するリポジトリやサービスを使用して、それぞれのエンティティに対応するテーブルが作成され、初期データが登録されます。さらに各エンティティの関連が意図した通りであることも確認できました。次回は日々のバイタル測定値を入力する画面を作成します。

ソースコード
GitHub からソースコードをダウンロードすることができます: branch2

(3)SpringMVC+Thymelef+Bootstrap で実用アプリの開発: 入力フォームと検証

$
0
0
前回のポストでは、アプリケーションの機能を定め、要件に対応するモデルとサービスを作成しました。3回目の今回は、起動時に当日のバイタルデータを入力する以下のようなビューを表示して、入力データの検証と保存を行います。

入力フォームの作成
com.itrane.healthcare.controller.HomeController.java を次のように変更します: editVod() メソッドは起動時のリクエスト "/"または メニューからの呼び出し "/updateVod"リクエストに応答して、ビュー(home.html)を表示します。コントローラーが各リクエストに応答する前に、@ModelAttribute 注釈付きのメソッド getHomeCmd( ) が実行されます。このメソッドはビューにモデルを受け渡すための HomeCmd オブジェクトを作成して、参照キー "homeCmd"で Model に登録します。 同時に HomeCmd の作成に必要な当日の Vod(1日分のバイタル)を取得します。当日の Vod がまだ登録されていない場合は新規作成して、データベースに保存します。簡単なビューの場合、Vod エンティティを直接渡すこともできますが、Vod と Vital エンティティは OneToMany の関連を持っています。これらを1つの form 内で処理したり、エラーメッセージの有無で画面表示を制御したいので、画面構造に合わせた HomeCmd を作成します。
com.itrane.healthcare.command.HomeCmd:HomeCmd は、Vod に対応するフィールドと Vital に対応する List<VitalCmd> フィールドを持っています。
com.itrane.healthcare.command.VitalCmd:VitalCmd は、Vital に対応するフィールドと画面制御に必要な VitalMst に対応する項目やエラーメッセージのための項目を持っています。
HomeCmd を参照するビュー(WEB-INF/home/home.html)は次のようになります:home.html は、参照する "homeCmd"を form の th:object 属性で指定します。
<tr> の th:each により vitalCmd を繰り返し参照して、バイタルの測定値を入力する <input> を表示します。

データの検証と保存
input の type 属性に、HTML5 の "number", "phone", "email"などを設定したり、エンティティの項目に @Max, @Min などの注釈を付けて、入力項目単体の検証を行うことができますが、実際のアプリケーションでは複数項目の入力値の比較や、データベースの検索などによる複雑な検証を行うことが必要です。この健康管理アプリの場合にも一定のルールに基づいた検証を行います。バイタルの測定場面を考えると同じ予定時間のものが同時に測定されるとは限りません。マスターの順序通りに測定されないこともあります。このアプリでは最低限、以下のルールを適用します。
  1. null または空文字は不可
  2. 1 以上の数値(本来は、バイタルマスターで最大値、最小値を設定すべき)
  3. 登録済みデータは入力があっても置き換えない(修正ボタンで個別に修正)
  4. 未登録データはデータ検証を行い適正な値なら保存する

"保存"ボタンを押すと、form の th:action で指定した URL "@{/updateVod}"がリクエストされ、HomeController#saveVod()が実行されます。ビューから返される HomeCmd("inputCmd")を、HomeCmd#checkErrorsAndUpdateVod(HomeCmd inputCmd) メソッドで検証します。入力にエラーがなければデータはが保存され次のような画面が表示されます:

エラーがあった場合は次のようになります:


ソースコード
ソースコードは GitHub からダウンロードできます:branch3

(4)SpringMVC+Thymelef+Bootstrap で実用アプリの開発:一覧表、チャートの表示

$
0
0
今回は、当日以前の1週間ごとのバイタルを表示できるようにします(画面1)。さらに表示期間のバイタルのグラフ(血糖値や血圧など)を表示する例を示します。
画面1:
フラグメントの変更
frag01.html を変更して、ナビゲーションバーフラグメント("navbar")の "バイタル管理"プルダウンメニューから "バイタル一覧"を表示できるようにします。また、チャートを Bootstrap の Modal ダイアログに表示するために、モーダル作成フラグメント("chart-create-modal")を追加します。
/DEF-INF/views/fragment/frag01.html:
コントラーラの作成
一週間ごとのバイタル一覧を表示するためのコントローラを作成します。
com.itrane.healthcare.controller.VodController: 上記のコードを簡単に説明します:
  • getVodList()メソッド: 当日から過去1週間分の Vodリスト(List<Vod>)を取得して、ビュー("/vod/vodList.html")を表示します。ビューに、Vodリストや画面制御情報を渡すために VodListCmd を作成して、Model および WebRequest(セッションスコープ)に保存します。
  • prevWeek(), nextWeek() メソッド: ビューの "前の週"、"次の週"ボタン(画面1を参照)を押すと実行されます。WebRequest(セッションスコープ)から VodListCmd を取得して、画面移動の有効/無効の判定や該当週のデータの取得を行います。VodListCmd を更新して、Model および WebRequest(セッションスコープ)に保存し、ビュー("/vod/vodList.html")を表示します。
  • showBsChart()メソッド: ビューの "血糖値"ボタン(画面1を参照)を押すと実行 されます。WebRequest(セッションスコープ)から VodListCmd を取得して、表示されている期間のバイタル情報を取得します。google chart を使用して、Visualization: Combo Chart(棒グラフ+折れ線グラフ)表示するために必要なデータを JSON 形式でビューに返します。

コントローラの各メソッドで使用する VodListCmd(com.itrane.healthcare.command.VodListCmd)を作成します: VodListCmd は以下のような情報を保持します。
  • prev: "前の週"ボタンの有効、無効
  • next: "次の週"ボタンの有効、無効
  • vms: VitalMst のリスト
  • vods: 該当期間の Vod のリスト
  • startDt: 該当期間の先頭の日付

ビューの作成
"/vodList"や "/prevWeek"リクエストに応答して、表示されるビューを作成します。
/WEB-INF/views/vod/vodList.html: vodList.html について簡単に説明します。
javascript パート:
  • javascript の src="https://www.google.com/jsapi"は、google chart を使用することを示します。
  • google.load("visualization", "1", { packages : [ "corechart" ] }); は Visualization : combochart を使用できるようにします。
  • "血糖値"ボタン(id=bsButton)を押すと、showBsChart 関数が実行され、jQuery の $.post により、VodController#showBsChart() が実行されます。
  • 呼び出しが成功すると、コールバック関数が呼ばれ、
       $('#myModal').modal('show') により Bootstrap の Modal ダイアログを表示して、ダイアログ内の 'chart_div'領域にチャートを表示します。

HTML パート:
  • body の th:object="${vodListCmd}"で Model に登録された、VodListCmd を参照します。
  • VodListCmd が保持する情報 "*{vods} "(該当週のバイタルデータ)を繰り返し処理して、測定日ごとの行を表示します。
  • 各行ごとに、"${vod.vitals}"(測定日のバイタル)を繰り返し処理して、列(マスターに設定された測定バイタルの種類)ごとの測定値を表示します。
  • フラグメントの "creat-chart-modal"をインクルードします。

実行結果
以下は画面1の状態で、"前の週"ボタンを押した場合の画面です。
画面2:
画面2の状態で、"血糖値"ボタンを押すと次のようにモーダルダイアログが表示されて、該当週の測定日ごとの血糖値の推移が表示されます。
画面3:


ソースコード
ソースコードは GitHub からダウンロードしてください: branche4

(5)SpringMVC+Thymelef+Bootstrap で実用アプリの開発: Excel ファイルのエクスポート、PDFの表示

$
0
0
Excel や PDF は、エンドユーザーコンピューティングでよく利用されます。そこで今回は、Excel ファイルや PDF をエクスポートする方法を説明します。画面1、画面2にそれぞれの出力例を示します。
画面1:PDF

画面2:Excel


ビューリゾルバの追加
Excel と PDF のためのビューリゾルバーを追加します。WebAppConfig.java を以下のように変更してください。
リスト1:com.itrane.healthcare.init.WebAppConfig: excelViewResolver は、excel ドキュメントビューのリゾルバです。XmlViewResolver クラスは、Spring の XMLビーンファクトリと同じ DTD を持つXMLベースの設定ファイルを受け入れる ViewResolver の実装です。XmlViewReslver#setLocation( ) メソッドは、ビュー定義 xml ファイルが置かれる場所とファイル名を指定します。明示的に指定しない場合、デフォルトとして /WEB-INF ディレクトリの "views.xml"が指定されます。
pdfViewResolver は、pdf ドキュメントビューのリゾルバで、ResourceBundleViewResolver クラスのインスタンスを返します。 ResourceBundleViewResolver#setBaseName( ) メソッドはビュープロパティ定義ファイルのベース名を指定します。

ビュープロパティの定義
pdfViewResolver で指定したビュープロパティ定義ファイル "views.propererties"をクラスパス内に作成します。
リスト2:src/views.properties: 上記の ".(class)"プロパティは pdfビュークラス(リスト8)を指定します。

excelViewResolver で指定した(上記の場合は暗黙指定)xml ベースのビュー定義ファイルを作成します。
リスト3:/WEB-INF/views.xml
フラグメントの変更
frag01.html の "navbar"フラグメントを変更して、メニュー項目 "エクスポート"を追加します。
リスト4:/WEB-INF/views/fragment/frag01.html:
コントローラの作成
エクスポート機能のためのコントローラを作成します。
リスト5:com.itrane.healthcare.controller.ExportController: "frag01.html : navbar"フラグメントを変更して追加した、"バイタル管理"プルダウンの"エクスポート"メニューを選択すると、上記コントローラの exportForm( ) メソッドが実行され、/WEB-INF/views/export/exportForm.html(画面3:エクスポートフォーム、リスト3)が表示されます。
画面3:エクポートフォーム


ビューの作成
ExportController#exportForm メソッドが返すビュー exportForm.html を作成します。
リスト6:/WEB-INF/views/export/exportForm.html: 上記ビューで、リンクの "エクスポート excel ドキュメント"をクリックすると、th:href 属性で指定した "@{downloadExcel}" url がリクエストされ、 ExportController#downloadExcel( ) メソッドを実行します。このメソッドでは、VitalMst と Vod を全件取得して、"vms"と "vods"の名前で ModelAndView オブジェクトに登録して、ビュー "excelView"を表示します。
リンクの "エクスポート pdf ドキュメント"をクリックすると、ExportController#downloadPdf( ) を実行します。このメソッドでも、VitalMst と Vod を全件取得して、"vms"と "vods"の名前で ModelAndView オブジェクトに登録します。ビューは "pdfView"を表示します。

ドキュメントビュー(excel/pdf)の作成
ExportController#downloadExcel( ) メソッドから呼ばれるビュー "excelView"を作成します。 excelViewResolver は、/WEB-INF/views.xml ビュー定義から、以下のビュークラスを解決します。
リスト7:com.itrane.view.ExcelBuilder: 上記コードは Apache POIを使って Excel ドキュメントを作成します。コントローラから渡されたデータ("vods"と "vms")を取得し、POI の API を使って Excel 形式のファイル(画面2)を作成してクライアントにダウンロードします(例:私の環境ではダウンロードフォルダに "downloadExcel.xls"というファイル名でダウンロードされます)。
ExportController#downloadPdf( ) メソッドから呼ばれるビュー "pdfView"を作成します。 pdfViewResolver は、/src/views.properties ビュー定義から、以下のビュークラスを解決します。
リスト8:com.itrane.view.PdfBuiler: pdf の作成には iTextを使用しています。Excel ドキュメントの場合と同様に、コントローラから渡されたデータ("vods"と "vms")を取得して、iText の Document, PdfWriter, PdfPTable オブジェクトを使って一覧表を出力します(画面1)。


ソースコード
ソースコードは GitHub からダウンロードしてください:branche5

(6)SpringMVC+Thymelef+Bootstrap で実用アプリの開発: Excel ファイルのインポート

$
0
0
このシリーズの(6)回目はファイルのインンポートについて説明します。この健康管理アプリの場合、選択した Excel ファイルをアップロードしてデータベーステーブルを更新します。

マルチパートリゾルバの追加
以下のように WebAppConfig を修正して、マルチパートリゾルバを追加します。
(リスト 6-1)com.itrane.healthcare.init.WebAppConfig.java: マルチパートリゾルバを追加する事で、Spring のマルチパート処理を利用できます。リクエストにマルチパートが見つかると、MultipartResolverが使われます。CommonMultipartResolver を使うには、commons-fileupload(pom.xml で追加済み)が必要です。

ビューの作成
インポート(アップロード)するファイルを選択するためのビューを作成します。
(リスト 6-2)/WEB-INF/views/import/importForm.html: 上のビューで、エクセルファイルが選択された場合はインポート処理を実行します。処理中は Bootstrap の "進捗バー"コンポーネントを使用して、進捗状況をユーザーにフィードバックします。インポート処理が成功すると成功メッセージが表示されます。
(画面 6-1)
ファイルタイプがエクセルファイルでない場合はエラーメッセージを表示します。
(画面 6-2)
Bootstrap 進捗バーを更新するために Ajax を利用します。jQuery.ajax() で、url に "importExcel"を指定して、コントローラ(ImportController)の upload() メソッドを呼び出します。 データ(data)にはフォームデータ(FormData)、データタイプ(dataType)には JSON形式、enctype には 'multipart/form-data'を指定します。
XMLHttpRequest をカスタマイズして "progress"イベントを監視して、setProgress() 関数で進捗バーを更新します。

コントローラの作成
インポート処理を行うためのコントローラを作成します。
(リスト 6-3)com.itrane.healthcare.controller.ImportController.java:
上記コントローラの importForm() メソッドは、インポートファイルを選択するビュー(importExcel.html)を表示します。
Spring のディスパッチャサーブレットがマルチパートリクエスト検出すると、マルチパートリゾルバ(リスト 6-1 を参照)がアクティブになり、HttpServletRequest を MultipartHttpServletRequest にラップします。 upload() メソッドでは、MultipartHttpServletRequest を使って、リクエスト内のマルチパートについての情報を取得します。取得したファイル情報を importExcelData() に渡してインポート処理を実行します。コードを簡単にするために、上記の例では想定した構造(シート名、シート数、列数など)のファイルが選択されたものとして処理を行っています。また apache poi の API を使って取得した行列の値をログに出力しているだけです(ログ 6-1)。
データベースを更新する例としては、以下のような仕様が考えられます:

  • インポート行の日付列の値に合致する "Vod"が登録済みであればその日のバイタルを入力行の対応する列の値で更新します。 
  • 日付列の値に合致する "Vod"が未登録の場合は、入力行の値で "Vod"を新規登録します。

インポート処理を呼び出せるようにメニューを変更します。
(リスト 6-4)/WEB-INF/views/fragment/frag01.html:

importForm に表示するメッセージを追加します。
(リスト 6-5)/WEB-INF/messages/messages.properties:

ソースコード
ソースコードは GitHub からダウンロードしてください: branche6

(7)SpringMVC+Thymelef+Bootstrap で実用アプリの開発: タスクスケジューラ

$
0
0
このシリーズ7回目の今回は、Spring のタスクスケジューラを使って薬の服用通知を行う処理を考えてみます。薬の飲み忘れ防止や処方通りの種類や量を守るのは、意外と面倒です。モバイル端末などで、決まった時間に通知を受けることができれば飲み忘れを防止できます。

服用通知のシナリオとしては以下のようなものが考えられます:
処方箋データの服用済フラグがオフで、服用目安時間を超えている場合は、薬の名前と量を定期的に(例えば10分間隔で)通知します。PCやスマホなどで通知を受けて薬を飲み終わったら服用チェック画面で服用済をチェックします。チェックを行わないと通知は繰り返されます。無駄な通信を防ぐために、次の通知(例えば昼食後通知)の時間を超えたらそれ以前の通知(例えば朝食後通知)は停止します。あるいはそれぞれの通知回数を制限するようにします。

通知タスクの作成
服用通知を行うためのタスクを作成します。
(リスト 7-1)com.itrane.healthcare.task.TakeMedTask.java:
上記コードは通知を確認するために、単にメッセージをログに出力しているだけです。実際のアプリでは複数のユーザーの複数の処方箋情報を調べて、上述したシナリオのような処理を実装する必要があります。


タスクスケジューラの設定
WebAppConfig を修正して、タスクスケジューラを設定します。
(リスト 7-2)com.itrane.healthcare.init.WebAppConfig.java
スケジューリングを有効にするために、@EnableScheduling を追加します。また、スケジュールの設定を行うために SchedulingConfigurer インターフェースを実装して、configureTasks() をオーバーライドします。このメソッドで、固定間隔(上の例では 5秒間隔)でスクジュールされたタスク(TakeMedTask)を登録します。ここでは、通知の確認を容易にするために 5秒間隔でスケジュールしていますが、cron ベースのスケジューリングでより詳細な指定が可能です。
アプリケーションを起動すると以下のような出力が確認できます。
(ログ 7-1)


ソースコード
ソースコードは GitHub からダウンロードしてください: branche7

(8)SpringMVC+Thymelef+Bootstrap で実用アプリの開発: セキュリティ(1)

$
0
0
このシリーズの最後は、Spring Securityを利用してアプリケーションを保護します。具体的には、アプリケーションの全ページ(全URL)に対して、ユーザー名とパスワードによるログインを要求します。LoginUser テーブルに登録されたユーザーのユーザー名とパスワードに一致すればそのログイン・ユーザーを認証します。このデモでは各ユーザーにロールとして、 "ROLE_USER"か "ROLE_ADMIN"またはその両方を設定します。ロールごとのアクセス権限の確認のために「ユーザー管理機能」(ユーザー一覧ビュー)を追加して、"ROLE_ADMIN"ロールを持つユーザーのみがこの機能にアクセスできるように設定します。

依存ライブラリの追加
Spring Security を使用できるように、pom.xml を次のように修正します。
(リスト8-1)pom.xml : thymeleaf-extras-springsecurity3 は、thyme leaf と Spring Security を統合するためのものです。

セキュリティ・サポートのためのイニシャライザーの追加
AbstractSecurityWebApplicationInitializer を拡張した、イニシャライザー・クラスを作成します。
(リスト8-2)com.itrane.healthcare.init.AppSecInit : このクラスは、アプリ内のすべての URL に対して、自動的に フィルタ (SpringSecurityFilterChain) を登録します。登録される主なフィルタには:
  • セッションにセキュリティ情報を設定するフィルタ
  • アクセス制御フィルタ
  • 認証処理フィルタ
  • ログアウト処理フィルタ
などがあります。

セキュリティ設定の追加
認証、認可、ログインフォームなどの設定を行うために、以下のような内容のセキュリティ設定クラスを作成します(各コードの詳細はコメント文を参照してください)。
(リスト8-3)com.itrane.healthcare.init.WebSecConfig :

ログイン処理サービスの作成
上記の WebSecConfig で認証プロバイダとして指定した LoginService を作成します。
(リスト8-4)com.itrane.healthcare.service.LoginService : このサービスはログインフォーム(login.html)で入力されたユーザー名(userName) に対して、Spring Security が提供する org.springframework.security.core.userdetails.UserDetails インスタンス(第一引数がユーザー名、第二引数がパスワード)を返すことで、そのユーザーを認証します。上記コードではまだデータベースに登録されたユーザーに対する認証をは行っていません(データベースに登録されたユーザーに対する認証は次回ポストで説明します)。

WebAppConfig の修正
アプリケーション設定クラスを修正して、上述のセキュリティ設定 (WebSecConfig) をインポートします。
(リスト8-5)com.itrane.healthcare.init.WebAppConfig : pom.xml に追加した thymeleaf の Spring Security 統合モジュールを利用できるように、templateEngine() メソッドで SpringSecurityDialect を追加します。これにより、thymeleaf で Spring Security の Taglib と同等の機能を利用できます。

AppInit の修正
アプリケーションの各ページに追加されたセキュリティ・フィルタ・チェインが、アプリケーション・コンテキストからセキュリティ設定情報を参照できるように、イニシャライザーを以下のように修正します。getRootConfigClasses() メソッドは指定した設定クラスに基づいてアプリケーション・コンテキストを作成します。
(リスト8-6)com.itrane.healthcare.init.AppInit :

ログインコントローラを作成
(リスト8-7)に示すコントローラは、ログイン、ログアウトの処理とアクセス権限のないページにアクセスした場合のエラー処理を行います。"/login"リクエストはセキュリティ設定で指定したログインフォーム(login.html)を表示します。"/logout"リクエストはログアウト処理を行います。
(リスト8-7)com.itrane.healthcare.controller.LoginController :

ログインフォームを作成
ユーザー名とパスワードを入力するログインフォームを作成します。
(リスト8-8)/WEB-INF/views/login.himl : error.html、logout.html は GitHub を参照してください。

ユーザー管理コントローラの作成
ユーザー一覧ビュー(userList.html)を表示するためのコントローラを作成します。
(リスト8-9)com.itrane.healthcare.controller.UserController : ここで重要な点は、このビューを表示するためのリクエストURL が "/admin/userList"となっていることです。セキュリティ設定で、 "/adimin/**"にアクセスできるのは "ROLE_ADMIN"を持つユーザーだけと指定しているため、このページ(ユーザー一覧ビュー)にアクセスできるのは管理者ロールのユーザーだけとなります。

ユーザー一覧ビューの作成
管理者(ROLE_ADMIN)だけがアクセス可能なユーザー一覧ビュー(userList.html)を作成します。
このビューはロールによるアクセス制御を確認するためのもので、今のところ何も表示しません。

メニューを修正
/WEB-INF/views/fragment/frag01.htm を修正して、メニューに「ユーザー一覧」と「ログアウト」を追加します。

実行結果
アプリケーションを起動すると、ログインページ(画面8-1)が表示されます。ユーザー名/パスワード( admin/admin )を入力するとホームページが表示されます。
(画面8-1)login.html:

メニューから「バイタル管理」>「ユーザー一覧(管理者)」を選択するとユーザー一覧(画面8-2)が表示されます。
(画面8-2)userList.html:

メニューから「ログアウト」を選択すると(画面8-3)が表示されます。
(画面8-3)login.html:


再度、user/user でログインして、「バイタル管理」>「ユーザー一覧(管理者)」を選択した場合、ロールが "ROLE_USER"のため、次の画面が表示されます。
(画面8-4)error.html:


ログインフォームの表示、ロールごとのアクセス制御はできるようになりました。しかしこの段階では、まだユーザーは LoginUser テーブルに登録できません。また、バイタルマスター(VitalMst) と1日の測定バイタル(Vod) は、ユーザーごとに管理する必要があります。これらのエンティティの修正、ユーザー管理機能の追加などは次回のポストで説明します。

ソースコード
GitHub からダウンロードしてください : branche8

(9)SpringMVC+Thymelef+Bootstrap で実用アプリの開発: セキュリティ(2)

$
0
0
今回は前回の続きとして、ログインユーザーをデータベーステーブルに登録/管理する機能を実装します。

個人情報エンティティの作成
このアプリの目的が健康管理なので、お薬手帳の個人情報テーブル(UserInfo)にログインユーザーを登録することにします。今のところ、個人情報エンティティにはログインに必要な項目だけ(ユーザー名、パスワード、ロールなど)を保持することにします。
(リスト9-1)com.itrane.healthcare.model.UserInfo:GitHub ソース対応するリポジトリ、サービス、サービス実装も作成します。
(リスト9-2)com.itrane.healthcare.repo.UserRepository:GitHub ソースfindPage()メソッドの引数 Pageable は、ユーザー一覧ビューの dataTables から Ajax 通信により送られたページ情報(ページ番号、表示行数、ソート方向など)を元に、表示ページのユーザーリストを取得するために使用します。

(リスト9-3)com.itrane.healthcare.service.UserService:GitHub ソース
(リスト9-4)com.itrane.healthcare.service.UserServiceImpl:GitHub ソース上記のコードでは、ユーザー名が "user1"のユーザーの場合だけ、登録ユーザーがなくてもログインできるようにしています。ユーザーを登録した後に "user1"以外でログインすると、UserRepository#findByName(ログインユーザー名) で検索されたユーザーのユーザー名/パスワードでログインを判定します(このデモでは起動時にダミーユーザーを登録しています。参照: GitHub ソース)。

ユーザーコントローラの修正
前回:セキュリティ(1)で作成した UserController を次のように修正します。
(リスト9-5)com.itrane.healthcare.controller.UserController: コントローラの各メソッドを簡単に説明します。

  • userList() : "/admin/userList"リクエストに応答して、ユーザー一覧ビューを表示します。このビューは管理者ロール(ROLE_ADMIN)のユーザーのみがアクセスできます。
  • getUserPage(): ユーザー一覧ビューの dataTablesに対するユーザー操作(次ページ、前ページ、ソートなどを行う)に対して、このメソッドが呼ばれます。dataTables から送られたクエリー文字列から、ページ情報(ページ先頭行のオフセット値、ページの表示行数、ソート列、ソート方向など)を取り出して、UserService#findPage() メソッドを実行して、表示ページのユーザー一覧を取得します。WebAppUtil.fromJson() メソッドが返す DtAjaxData クラスを以下に示します。
    (リスト9-6)com.itrane.common.model.DtAjaxData:
    DtAjaxData 内の DtColumns, DtOrder, DtSerarch は GitHub のソースを参照してください。
    (リスト9-7)com.itrane.common.model.DtColumns: GitHub ソース
    (リスト9-8)com.itrane.common.model.DtOrder: GitHub ソース
    (リスト9-9)com.itrane.common.model.DtSearch: GitHub ソース
  • save() : このメソッドは、ユーザー一覧ビュー(userList.html) から Ajax 通信により、ユーザー情報(JSON文字列)を受け取ります。JSON文字列は @RequestBody 注釈によって自動的に UserCmd 型に変換されます(参照:userTable.js)。ユーザー情報(cmd.user)の入力値を検証して、エラーがなければ UserService#save() により、user を保存します。ステータス、エラーメッセージは UserCmd にセットして、ユーザー一覧ビューに返します。

ユーザー一覧ビューの修正
コントローラ同様、前回作成したユーザー一覧ビュー(userList.html)も次のように修正します。
(リスト9-10)/WEB-INF/views/user/userList.html: ユーザー一覧ビューが読み込む userTable.js を以下に示します。
(リスト9-11)/WebContent/js/userTable.js: GitHub ソースユーザー一覧ビューの「追加」ボタン /「編集」ボタンを押すとユーザー情報を入力するフォームを含むモーダル(ユーザー情報編集モーダル)を表示します。モーダルの「保存」ボタン $('#btnSave') を押すと、入力フォームの input 要素の値を  JSON 文字列として、jQuery の $.ajax 関数で UserController#save()  へ送信します。通信が成功すると、返された cmd の status を調べて、保存に成功した場合はモーダルを閉じて、テーブルを再描画します。

ユーザー一覧ビューで使用されるフラグメントを作成します。
(リスト9-12)/WEB-INF/views/fragment/fragModal.html:GitHub ソース
(リスト9-13)/WEB-INF/views/fragment/fragModalBody.html:GitHub ソース
これらのフラグメントはユーザー情報編集モーダルを表示するために使用されます。

セキュリティ設定の修正
LoginService の代わりに、上で作成した UserService を使用するようにセキュリティ設定を修正します。
(リスト9-14)com.itrane.healthcare.init.WebSecConfig: GitHub ソース
WebAppConfig を修正して、WebContent/js, WebContent/css フォルダ下のリソースを参照できるようにします。
(リスト9-15) com.itrane.healthcare.init.WebAppConfig:GitHub ソース

ここまでの変更で、管理者としてログインしてユーザー一覧ビューからユーザーを登録/修正することができるようになります。また、個人情報テーブル(UserInfo)に登録されたユーザーはユーザー名/パスワードでログインが可能です。各ユーザーはログイン後、個人情報の編集フォームからユーザー名/パスワードを変更できるようにすべきですが、このデモでは実装していません。
複数のユーザーがアプリケーションを利用できるように変更したため、バイタルマスター(VitalMst) や測定データ (Vod) をユーザーごとに管理できるようにする必要があります。変更後のエンティティ間の関係は次のようになります。
(図9-1)エンティティ関連:
エンティティの変更に合わせて、VitalMstRepository、VodRepository を変更します。さらに、対応するサービスやコントローラも変更する必要があります(詳細は GitHub: healthcare branche9でソースを確認してください)。

修正結果の確認
起動時にあらかじめ登録したユーザー user1(パスワード:user1,  ロール:管理者) でログインしてバイタル一覧 を表示すると次の画面が表示されます。一覧の列は user1 のバイタルマスターの設定に依存します(起動時に初期化しています: InitMasterを参照)。
(画面9-1)バイタル一覧/ user1 でログイン:

メニューの保守/ユーザー一覧を選択してユーザー一覧ビューを表示します。
(画面9-2)ユーザー一覧ビュー (userList.html):
上の画面で追加ボタンを押すとユーザー編集モーダルが表示されます。user6, user7 を追加すると次のようになります。
(画面9-3)ユーザー一覧ビュー/user6, user7 を追加:

再度、追加ボタンを押して、ユーザー名が user2 のユーザーを追加しようとすると UserInfo の name フィールドの @Column(unique=true) 注釈により、ユーザー名重複エラーとなり次の画面が表示されます。
(画面9-4)ユーザー一覧ビュー/新規追加モーダル/重複エラー:

編集ボタンを押し user2 を編集して名前を use に変更すると検証エラーが表示されます。
(画面9-5)ユーザー一覧/修正モーダル/検証エラー:

ログアウトして、 user3 (ROLE_USER)でログインしてバイタル一覧を表示すると user3 に対するバイタルマスターが反映されて、列の表示は user1 とは異なるものになります。
(画面9-6)バイタル一覧/ user3 でログイン:

保守/ユーザー一覧メニューを選択しても user3 は管理者ロールではないので、ユーザー一覧ビューにはアクセスできません。


HTML5 video で字幕付きビデオを表示する

$
0
0
以前、vlcj を使ってビデオプレーヤーを作成しましたが、今回は HTML5 の video 要素を使ってビデオを(字幕付きで)表示するWEBアプリを作成したいと思います。

開発環境

  • OS: OS X Yosemite 10.10.3
  • IDE: Eclipse Luna SR2(4.4.2) Java EE パッケージ
  • サーバー:Tomcat 7.0.61
  • ブラウザ:Safari 8.0.5

 注意:現在 video 要素のサポート状況はブラウザによって異なります。このデモアプリは開発環境および iOS の Safari と android の Chrome でのみ動作を確認しています。主要なブラウザの最新バージョンでは、基本機能の大部分をサポートしているようです。字幕を表示するための track 要素のサポート状況もかなり良いと思います。

新規プロジェクトの作成
WEBアプリ作成のフレームワークとして、Spring MVC + Thymeleaf を使用します。新規プロジェクトの作成方法は「Spring MVC + Thymeleaf による WEB アプリケーション開発(1)プロジェクト作成」や「(1)SpringMVC+Thymelef+Bootstrap で実用アプリの開発: eclipselink を使用するDB設定」を参照してください。

  • プロジェクト名:myviewer
  • groupId:com.itrane
  • artifactId:myviewer


事前準備:動画情報ファイルの作成
このアプリで表示するビデオの情報(ビデオファイルのパス、タイトル、字幕ファイルのパスなど)を JSON形式で事前に作成します(指定フォルダ以下をスキャンして必要な情報を抽出するツールを作成して行いました)。
作成された動画情報ファイル (videodef.json):
  • videoPath:/contents/video に対するビデオファイルの相対パス
  • imgPath:サムネール表示用の画像ファイルの相対パス
  • trackPath:日本語字幕ファイル(WebVTTフォーマット:xxx.vtt)の相対パス。英語字幕は xxxe.vtt 。vtt ファイルは srt ファイルがあれば簡単に作成できます。

このファイルから動画情報を読み込むクラスは以下のようなものです:

Tomcat の aliases を設定する
現在のマシン環境(動画ファイルの保存場所)を変更したくなかったので、Tomcat の context.xml で、aliases を指定しました。例えば、実際のビデオコンテンツのルートフォルダが "/Users/foge/_contents"で、これをアプリから "contents"で参照したい場合は次のようにします:
コントローラの作成
com.itrane.myviewer.controller.VideoController: メソッドの説明
  • videoList():
    リクエストURL "/"または "/videoList"に応答して実行します。type パラメータに応じて決定されるビデオ情報ファイル(例」type が  "smallville"の場合 /Users/foge/_contents/video/smallville/videodef.json")から、 getVideoGroups() メソッドによりビデオ情報を取得して、ビデオ一覧ビュー(videoList.html)を表示します。  
  • videoPlayer():
    ビデオ一覧ビューでビデオを選択するとこのメソッドを実行します。ModelAndView オブジェクトにタイトル(title)、ビデオパス(vpath)、字幕パス(track)などをセットして、ビデオ再生ビュー(videoPlayer.html)を表示します。

ビューの作成
 ビデオ一覧ビュー:WEB-INF/views/video/videoList.html:上のコードで一番目の th:each 属性の ${videoCmd.videoGroups} は指定フォルダ下の全グループの情報を表し、grp は1グループの情報を表します。二番目の th:each 属性の ${grp.videoDefList} はグループ内の全ビデオ情報ファイル、vdef は1つのビデオの情報(videoDef インスタンス)に対応します。これらの各ビデオ情報は "a"タグの th:href 属性で、タイトル、ビデオパス、字幕パスなどのパラメータとして、コントローラの videoPlayer() メソッドに送られ、対応するビデオが再生されます。
以下にビデオ再生ビューの内容を示します。
ビデオプレーヤービュー: WEB-INF/views/video/videoPlayer.html:
実行結果
(画面1)ビデオ一覧を表示
(画面2)デフォルトの日本語字幕を表示
(画面2)メニューで英語字幕に変更

(画面3)iPad でビデオ一覧を表示


ソースコード
ソースコードを GitHub からダウンロードしてください:myviewer




(1)Spring Boot で Web アプリケーションを開発:プロジェクトの作成

$
0
0
2014年4月にリリースされた Spring Bootは、現在 1.2.3.RELEASE が提供されています。Spring Boot の紹介文には「スタンドアロンで製品品質の Spring ベース・アプリケーションを簡単に作成して、すぐに実行できる」とあります。以前のポスト「(1)SpringMVC+Thymelef+Bootstrap で実用アプリの開発」で作成したアプリと同様の機能を Spring Boot を使って実現してみたいと思います。

開発環境
  • OS: OS X Yosemite 10.10.3
  • IDE: Eclipse Mars M6 Java EE パッケージ
  • Maven:3.1.1
  • Java:Java SE 7

プロジェクトの作成
Eclipse の Java パースペクティブのパッケージ・エクスプローラで
  1. メインメニューから File -> New -> Maven プロジェクトを選択します。
  2. 表示された新規 Maven プロジェクトのウィザードで
    "Create a simple project"をチェックして next をクリック。
  3. 表示されたページで以下の内容を設定して finish をクリック。
    Artifact: Group id = com.itrane
    Artifact id = SpBootDemo
    Version = (0.0.1)
    Packaging = (jar)
    Name = SpBootDemo
  4. 作成されたプロジェクトで pom.xml を開いて次のように修正:
  5. プロジェクトを右クリックして、 Maven -> Update Project... を選択。
    pom の修正を反映させるために必要です。
雛形プロジェクトとしての最低限の機能を実装
  1. src/main/java/com.itrane.spbootdemo.app.App クラスを作成:
  2. 上記 App クラスが参照しているプロパティファイルを作成します。
    src/main/resources/app.properties を作成: 開発中は spring.thymeleaf.cache を false に設定することで、html の変更を再起動しないで反映できます。
  3. src/main/java/com.itrane.spbootdemo.controller.HomeController クラスを作成:
  4. 上記コントローラの home() メソッドで表示されるビューと、テンプレート(フラグメント)を作成します。
    ビュー src/main/resources/templates/views/home.html を作成: フラグメントsrc/main/resources/templates/fragment/frag01.htmlを作成:
  5. ロギングを logback に変更します。
    src/main/resources に logback.xml を作成:(GitHub ソースコード
  6. home.html、frag01.html から参照される messages プロパティを設定します。  src/main/resources/messages/messages.properties を作成:
    com.itrane.springbootdemo.app.AppConfig を作成:
  7. 静的リソースを設定します。
    src/main/resouces/static/css/app_base.css を作成:(GitHub ソースコード
    src/main/resources/static/js フォルダを作成。
以上の結果プロジェクトの構造は次のようになります:

アプリケーションの実行
アプリケーションを実行するには色々な方法があります。
1)App.java を右クリックして、Run As -> Java Application を選択します。
2)プロジェクトを右クリックして、Run As -> Maven build... を選択。
   表示された実行構成の Goals に spring-boot:run を入力して Run をクリック。
3)プロジェクトを右クリックして、Run As -> Maven install を選択します。
   プロジェクトの target フォルダに SpBootDemo-0.0.1.jar が作成されます。
   java -jar プロジェクトフォルダ/target/SpBootDemo-0.0.1.jar で実行します。

 1)の方法で実行して、ブラウザで http://localhost:8080/ を入力すると以下の画面が表示されます:

上述のプロジェクトに追加したい機能に応じて、モデル、コントローラ、ビュー、ビーンなどを追加することで、「(1)SpringMVC+Thymelef+Bootstrap で実用アプリの開発」で紹介したような Web アプリケーションを簡単に作成することができそうです。

ソースコード
ソースコードを GitHub からダウンロードしてください:SpBootDemo

(2)Spring Boot で Web アプリケーションを開発:データベースアクセス

$
0
0
前回に続いて、Spring Boot を使って Web アプリケーションを開発します。今回はデータベースアクセス機能を実装します。

データベース設定
/src/main/java/com.itrane.spbootdemo.app.DbConfig.java を作成します: @EnableJpaRepositories の設定以外は「(1)SpringMVC+Thymelef+Bootstrap で実用アプリの開発」の DbConfigと同じです。

/src/main/java/com.itrane.spbootdemo.app.AppConfig.java を次のように修正します:

モデルの作成
データベースに登録するユーザーエンティティを作成します。
/src/main/java/com.itrane.spbootdemo.model.User.java : 検証機能をテストするために、name に @Length 注釈を設定します。

リポジトリとサービスを作成
上述のユーザーエンティティを操作するリポジトリとサービスを作成します。
/src/main/java/com.itrane.spbootdemo.repo.UserRepository.java: このデモで使用する最低限の機能を実装します。

/src/main/java/com.itrane.spbootdemo.service.UserService.java:( GitHub ソース )
/src/main/java/com.itrane.spbootdemo.service.UserServiceImpl.java:
コントローラの作成
ユーザーの一覧表示、新規作成、修正などの処理を行うコントローラを作成します。 /src/main/java/com.itrane.spbootdemo.controller.UserController.java: コントローラメソッドの説明:
  • userList():ユーザー一覧ビュー(views/userList.html)を表示します。
  • getUserPage():userList.html の dataTables から ajax 通信で送られた情報を元にテーブルに表示するユーザーデータをDB (user テーブル)から取得します。
  • createUser():メニューの保守/ユーザー登録を選択するか、userList.html の"追加"ボタンを押すとこのメソッドが実行され、ユーザー編集ビュー(views/userForm.html)を表示します。
  • editUser():ユーザー一覧(userList.html)の任意の行のの"編集"ボタンを押すとこのメソッドが実行され、渡された id のユーザーの編集ビュー(views/userForm.html)を表示します。
  • saveUser():ユーザー編集ビューで"保存"ボタンを押すとこのメソッドが実行されます。@Valid の付いた user はこのメソッドが呼ばれる前に入力値が検証されます。入力値が正しい場合(!resutl.hasError())は、UserService#save() により user を保存して、新規モードの場合はユーザー編集ビューを修正モードの場合はユーザー一覧ビューを表示します。エラーの場合は元の画面(ユーザー編集ビュー)へ戻ります。

ビューの作成
ユーザー一覧ビュー /src/main/resources/templates/views/userList.html:
上記ビューで使用する javascript  ファイルを作成します。
/src/main/resources/static/js/userTable.js:
ユーザー編集ビュー /src/main/resources/templates/views/userForm.html:
上記ビューで使用する javascript  ファイルを作成します。
/src/main/resources/static/js/userForm.js:( GitHub ソース )

ビューの追加に合わせて、mesages.properties ファイルも修正します。

実装結果を確認する
App.java を右クリックして、Run As  Java Application を選択してアプリケーションを起動します。アプリケーションを起動すると、app.properties で設定した開発時の ddl-genaration=drop-and-create-tables により、User エンティティに対応する user テーブルが作成されます。
続いて、メニューから保守/ユーザー一覧を選択してユーザー一覧ビューを表示します。このデモアプリでは user テーブルにデータが1件も無い場合はテスト用のデータを3件追加します。次に示すユーザー一覧ビューが表示されれば、UserRepository が正しく機能していることが確認できます。
(画面1)ユーザー一覧ビュー:

ユーザー一覧で2行目の編集ボタンをクリックして、ユーザー編集ビューを表示します。
(画面2)ユーザー編集ビュー/修正モード:

データを修正して保存ボタンを押すと、データが保存され、ユーザー一覧が表示されます。
(画面3)ユーザー一覧/user2 の氏名変更後:


上記画面で追加ボタンを押すか、メニューの保守/ユーザー登録を選択して、ユーザー編集ビューを表示します。
(画面4)ユーザー編集ビュー/新規登録:
上記画面で連続して、user4、user5 を追加して、ユーザー一覧画面を表示します。
(画面5)ユーザー一覧/2ユーザー追加後:
(画面6)ユーザー編集画面で入力チェックエラーの場合
まとめ
ビューや静的リソースを置く場所を除けば、Spring MVC で Web アプリを開発する場合と全く同じです。実際、大部分のコードは「(1)SpringMVC+Thymelef+Bootstrap で実用アプリの開発」で作成したコードをコピーして使用しました。

ソースコード:GitHub SpBootDemo_b2

(3)Spring Boot で Web アプリケーションを開発:ホームサーバーで実行

$
0
0
今回は、前回作成した Spring Boot アプリケーションを jvm が動作するマシンで簡単に実行できるか確認します。我が家のホームサーバー(ASUSTOR AS-202TE)では Tomcat7 と MySQL が動いているので、WAR ファイルでデプロイする方が簡単ですが、JAR ファイルとして実行するのも極めて簡単です。

ポートの変更
JAR を作成する前に、ホームサーバーの Tomcat とぶつからないように、アプリのポート(デフォルトでは 8080)を 9000 に変更します。実行時にコマンドパラメータを使って変更することもできますが、このデモでは app.properteis で設定します。
/src/main/resources/app.properties に次の行を追加します:
server.port: 9000

JARファイルの作成
Eclipse のパッケージエクスプローラビューで SpBootDemo プロジェクトを選択して、次の方法で作成します:

  • 方法1:
    • コンテキストメニューから Run As -> Maven build... を選択。
    • 表示された起動設定ダイアログの Goals フィールドに "clean package"を入力して Run ボタンをクリック。
  • 方法2:
    • コンテキストメニューから Run As -> Maven clean を選択。
    • コンテキストメニューから Run As -> Maven install を選択。

プロジェクトの taget フォルダに SpBootDemo-0.0.1.jar が作成されます。
このファイルをASUSTORサーバーの適当なディレクトリにコピーします。

サーバーのデータベースの作成
OSX のターミナルから ssh でサーバーにログインします:
ターミナル$ ssh -l admin 192.168.179.8

このアプリは spbootdb スキーマにユーザー/パスワード demo/pass でアクセスします。データベースを使用しないアプリでは、次の作業は不要です。
root ユーザーでログインして spbootdb スキーマ、 demo ユーザーを作成します:
ssh> mysql -u root -p

mysql> CREATE DATABASE spbootdb DEFAULT CHARACTER SET utf8;
mysql> CREATE USER 'demo'@'localhost' IDENTIFIED BY 'pass';
mysql> GRANT ALL ON *.* TO 'demo'@'localhost';


アプリケーションの実行
サーバー上でアプリケーションを実行します:
ssh> java -jar /jarをコピーしたディレクトリのパス/SpBootDemo-0.0.1.jar

実行結果の確認
ブラウザで URL に"サーバーアドレス:9000/userList"を入力します。


(4)Spring Boot で Web アプリケーションを開発:WebRTC を使って写真を撮る

$
0
0
WebRTC(Web Real-Time Communications)フレームワークを使うことで、ブラウザにプラグインを追加することなく、ハードウェアのカメラやマイクにアクセスすることができます。現時点で、WebRTC を使うための getUserMedia() API は Chrome 21+、Opera 18+、Firefox 17+ でサポートされていますが、残念ながら Safari や iOS のブラウザでは使用できません。

今回は、WebRTC を使って Spring Boot Web アプリで写真を撮り、画像をデータベースに保存する方法を説明します。「(2)Spring Boot で Web アプリケーションを開発:データベースアクセス」のコードに対して以下の変更を行います。

モデルの変更
User エンティティにユーザーの画像データを Base64 文字列として保存するためのフィールドを追加します。
com.itrane.spbootdemo.model.User.java:

ビューの変更
上で追加した jpegString の画像データをビューに表示できるように userForm.html を次のように変更します。
src/resources/templates/views/userForm.html:

<input hidden="true" id="base" th:field="*{jpegString}" type="text" />
は、カメラから取り込んだ画像をBase64形式で保存するためのものです。
<img th:src="@{'data:image/jpeg;base64,'+*{jpegString}}"
width="160" height="120" id="userPhoto" />
は、User の jpegString の内容を画像として表示します。
<div th:with="nestFrag='camera-area',modalFooter='form-modal-footer',modalLg='false'"
th:include="fragment/fragModal :: create-modal"></div>
の部分は、ユーザーの写真(id="userPhoto")がクリックされた時にカメラ映像を表示するための Bootstrap モーダルを表すフラグメントを読み込みます。
以下は、camera-area, form-modal-footer, create-modal の各フラグメントの内容です。
src/resources/templates/fragment/fragModal.html:
上記コードで重要な部分は、camera-area フラグメントの video 要素と canvas 要素です。この video 要素にカメラの映像が表示され、クリックした時点のスナップショットが canvas に描画されます。getUserMedia() API を使って、この処理を行う javascript コードを以下に示します。
src/resources/static/js/userForm.js:

navigator.getUserMedia({video: true, audio: false},
...
の部分は、getUserMedia() API により、ハードウェアのウェブカメラにアクセスします。アクセスに成功すると
video.src = window.URL.createObjectURL(stream);
により、video 要素のソースとして、カメラを設定します。そして、video 要素をクリックすると takeSnapshot() 関数を実行して、カメラの映像を canvas に描画します。

結果を確認する
アプリケーションを起動して、http://localhost:9000/ にアクセスして、メニューから保守/ユーザー一覧を選択し、1番目のユーザーを編集します。
(画面1)ユーザー編集フォーム:まだユーザーの写真データは登録されていないので、画像は表示されません:

上の画面で写真の表示エリアをクリックすると、次の画面が表示されます。
(画面2)カメラを使っていいか許可を求めるダイアログが表示されます:


上の画面で"許可"ボタンを押します。
(画面3)カメラ映像のモーダルが表示されます。カメラ映像の表示域をクリックすると、スナップショットが得られます:

スナップショットが気に入ったら、"保存"ボタンを押します。
(画面4)スナップショットがユーザー編集フォームに表示されます:

ソースコード: GitHub SpBootDemo_b3_a

EGit で Bitbucket を使ってみた

$
0
0
以前のポスト「EGit によるソースコード管理(1) ローカルリポジトリの作成」でも書きましたが、私の場合はプロジェクトのソース管理はかなり単純なもので、現在は次のような Git 環境で管理しています。
  • GitHub : 公開リポジトリ
  • ホームサーバー(Git 1.8.3.1) : 非公開リポジトリ
  • Dropbox : 非公開リポジトリ
今の環境に不満はないのですが、Bitbucketは非公開リポジトリの作成に制限がない上、接続ユーザーも5人までは無料です。小規模プロジェクトで非公開のリポジトリを利用したい場合にはぴったりのサービスです。以下に Bitbucket を導入して、EGit から使用するまでを簡単に紹介します。

サインアップ
まず Bitbucket にサインアップします。

サインアップしたら、アカウントの管理、リポジトリの作成、チームの作成などが行えます。

リポジトリの作成
続いて、リポジトリを作成します。

非公開リポジトリを作成するため、アクセスレベルを非公開にしました。自分のプロジェクトにあわせて、リポジトリタイプを Git、プログラミング言語を Java にしました。

Eclipse で作成したプロジェクトをリポジトリにプッシュ 
すでにローカルリポジトリが作成されているとして話を進めます(「EGit によるソースコード管理(1) ローカルリポジトリの作成」を参照してください)。Git リポジトリビューで、ローカルリポジトリの git.mycontact を選択して、コンテキストメニューから Remote > Push.. を選択します:

次のウィザードが表示されます:

上の画面で URL 欄に先に作成した Bitbucket の mycontact リポジトリを指定します。サインアップで登録したユーザー名とパスワードを入力して、Next をクリックします。

"Source ref", "Destination ref"を設定(上図では refs/heads/master を選択)して、"Add Spec"ボタンを押し、"Next"をクリックします。リモートへプッシュが成功すると次のダイアログが表示されます。

Bitbucket にログインして、mycontact リポジトリを表示すると次の画面が表示されます:

左側のメニューから、ソース、コミットやブランチの状況を見ることができます。クローン、プルリクエスト、ダウンロードなどを行うこともできます。

チームの作成
複数のメンバーで(無料は5人までですが)開発する場合はチームを作成することができます。


リポジトリのインポート
GitHub の公開リポジトリをインポートして、簡単に非公開リポジトリを作成することができます。


クローンの作成
EGit を使って、Bitbucket の myconact リポジトリからクローンを作成します。以下のリポジトリビューでクローン作成ボタンをクリックします。

次のクローン作成ウィザードで、URL に Bitbucket の mycontact リポジトリを指定して、"Next"をクリックします。
内容を確認するため、"Next"をクリックして次の画面を表示します。


上の画面で "Finish"をクリックするとリポジトリビューに次のクローンリポジトリが表示されます:

このリポジトリを選択して、コンテキストメニューから Import projects.. を選択します。次の画面のように設定して "Next"をクリックします。

次の画面で、"Finish"をクリックすれば、Eclipse プロジェクトがインポートされます。

各開発者はローカルにコピーリポジトリを作成することで、自分自身の履歴を管理することができます。
Viewing all 71 articles
Browse latest View live