1. 概要
Java/JSPを利用して実装されたサンプルアプリケーションです。
2. セットアップ
サンプルアプリケーションのセットアップに関する説明です。
もし、既にiPLAssの開発環境の構築とチュートリアルを実行できており、サンプルアプリケーションを動かしたい場合、以下の手順に従って実施してください。
それ以外の場合は、開発環境の構築を先に実施することをお勧めします。
- 
サンプルアプリケーションのプロジェクトをGitHubから取得してプロジェクトを作成します。プロジェクトの作成手順はプロジェクトの作成を参照してください。 サンプルコードで Java21 より上のバージョンのスタイルでコーディングする場合は build.gradle の javaVersion を使用したい Java のバージョンに書き換えてください。 
 書き換えた後はGradleプロジェクトのリフレッシュを行い、エラーが出ないことを確認してください。build.gradleapply plugin: 'java' apply plugin: 'war' apply plugin: 'eclipse-wtp' ext { javaVersion = JavaVersion.VERSION_21 (1) } ----------------------------------------以下略----------------------------------------1 必要に応じて使用したい Java のバージョンに書き換えます。 
- 
テナントの作成 このサンプルアプリは開発環境の構築の章で作成したテナントで動かすことを想定しています。もし新規テナントで サンプルアプリを起動したい場合、以下の手順を実施してください。 - 
mtp-service-config.xmlに以下の設定を入れます。 <!-- Rdb Connection Settings --> <service> <interface>org.iplass.mtp.impl.rdb.connection.ConnectionFactory</interface> <!-- DataSource base ConnectionFactory --> <!-- <class>org.iplass.mtp.impl.rdb.connection.DataSourceConnectionFactory</class> --> (1) <!-- <property name="dataSourceName" value="java:comp/env/jdbc/iplass" /> --> (1) <!-- DriverManager base ConnectionFactory --> <class>org.iplass.mtp.impl.rdb.connection.DriverManagerConnectionFactory</class> (2) <!-- for mysql --> <property name="url" value="jdbc:mysql://[host]:[port]/[schema]" /> <property name="user" value="XXXXX" /> (3) <property name="password" value="XXXXX" /> (3) <property name="driver" value="com.mysql.cj.jdbc.Driver" /> (4) <!-- if sql execution exceeds this milliseconds, log sql by warn level.(0 means no log by warn level) --> <property name="warnLogThreshold" value="0" /> <!-- If change transaction isolation level on create connection, set below. --> <!-- <property name="transactionIsolationLevel" value="READ_COMMITTED" /> --> </service>1 データソース利用に関する設定項目をコメントアウトします。 2 DriverManager利用に関する設定項目を追加します。 3 テナント作成時はDBA権限を持つユーザーを設定してください。 4 クラスパスが通っている場所にJDBCドライバを配置します。 
- 
サンプルアプリのルートパスの下で 、 - 
Gradleタスクの runTenantBatchを実行します。
- 
起動した画面で Create Default Tenantをクリックします。
- 
ダイアログで name(テナント名)、AdminUserId(管理者ユーザーID)、AdminUserPassword(管理者ユーザーパスワード)を入力し、Createをクリックします。
 
- 
 
- 
| サンプルアプリでは、データベースへの接続方法にDataSourceを利用することを想定していますが、テナントを作成する処理の runTenantBatchタスクではDataSourceに関する設定を利用できないため、一時的に接続方法をDataSource利用からDriverManager利用に変えます。テナント作成後は、再度DataSourceを利用する設定に戻してください。 | 
- 
以下のサンプルアプリの起動手順を順に実施してください。 
- 
サンプルアプリでは、DataSourceを利用するため、mtp-service-config.xmlでDataSource名を設定します。また、Tomcatのコンテキスト設定にDataSourceの接続情報を入れます。 
 また、Tomcatのlibフォルダに利用するDBに該当するJDBCドライバを入れます。ファイル名 /src/main/resources/mtp-service-config.xml <!-- Rdb Connection Settings --> <service> <interface>org.iplass.mtp.impl.rdb.connection.ConnectionFactory</interface> <!-- DataSource base ConnectionFactory --> <class>org.iplass.mtp.impl.rdb.connection.DataSourceConnectionFactory</class> <property name="dataSourceName" value="java:comp/env/jdbc/iplass" /> (1) <!-- if sql execution exceeds this milliseconds, log sql by warn level.(0 means no log by warn level) --> <property name="warnLogThreshold" value="0" /> <!-- If change transaction isolation level on create connection, set below. --> <!-- <property name="transactionIsolationLevel" value="READ_COMMITTED" /> --> </service>1 サンプルアプリ実行用のDataSource名 開発環境の構築の章で作成したデータベースの接続情報をインストールしたTomcatの次の設定ファイルに入れます。 %CATALINA_HOME%\conf\context.xml<?xml version="1.0" encoding="UTF-8"?> <WatchedResource>WEB-INF/web.xml</WatchedResource> <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource> <Resource name="jdbc/iplass" auth="Container" type="javax.sql.DataSource" maxTotal="100" maxIdle="30" maxWaitMillis="10000" username="XXXXX" password="XXXXX" driverClassName="com.mysql.cj.jdbc.Driver" url="jdbc:mysql://[host]:[port]/[schema]"/> (1) </Context>1 Tomcatのコンテキスト設定に該当するデータベースの接続情報を入れます。 
- 
サンプルアプリでは、全文検索機能(lucene)を利用するため、mtp-service-config.xmlに記載のあるINDEXファイルの保存場所をローカル環境の適当な場所に変えます。 ファイル名 /src/main/resources/mtp-service-config.xml <service> <interface>org.iplass.mtp.impl.fulltextsearch.FulltextSearchService</interface> <property name="useFulltextSearch" value="true" /> (1) <property name="maxRows" value="1000" /> <property name="throwExceptionWhenOverLimit" value="true"/> <!-- lucene利用 --> <class>org.iplass.mtp.impl.fulltextsearch.lucene.LuceneFulltextSearchService</class> <property name="directory" value="[set your lucene index file store path. eg: D:\tmp\lucene]" /> (2) <property name="analyzer" value="org.apache.lucene.analysis.ja.JapaneseAnalyzer" /> <property name="indexWriterRAMBufferSizeMB" value="64.0"/> <property name="redundantTimeMinutes" value="10"/> </service>1 useFulltextSearchをtrueに設定します。 2 INDEXファイル保存場所をローカルパスの適当な場所に設定します。 
- 
Tomcatを起動し、 http://localhost:8080/コンテキスト名/テナント名/gem/にアクセスすると、ログイン画面が表示されます。管理者ユーザーIDとパスワードでログインします。
- 
Admin Consoleの Packaging機能を利用して、プロジェクトのsample-dataフォルダにあるサンプル実行用のデータをインポートします。「entitydata.zip」ファイルを、Admin Consoleの「Packaging」ツールで取り込んでください。   デフォルトの設定で「Import」ボタンを押下してください。   Importを実行すると「Log」パネルに処理状況が表示されます。処理が終了したタイミングでエラーが発生していないことを確認してください。   新たに作成したロールなどを反映させるために、MetaDataSettingsメニューの右上の「MetaDataのリフレッシュ」アイコンをクリックします。   
- 
Admin Consoleのテナント情報の多言語利用設定で、「日本語」と「English」にチェックを入れます。   「Save」ボタンを押してください。   
- 
Admin ConsoleでEntityExplorerの Entity Crawl機能を利用して全文検索のINDEXデータを作成します。
 詳細については、全文検索を参照してください。ToolsのEntityExplorerを選択します。   全文検索を利用する設定になっている場合、「Entity Crawl」タブが表示されます。   A) 任意のEntityのみを対象としてクローリングしたい場合は、リストの対象Entityにチェックを入れて、「Start Crawl」ボタンをクリックしてください。 B) クローリング対象Entityをすべてクローリングしたい場合は「Re Crawl All Entity」ボタンをクリックしてください。 C) クローリングが完了したら、最新のINDEXデータを反映させるために、「Refresh」ボタンをクリックしてください。   
- 
上記起動手順の実施が完了しましたら、サンプルアプリのグローバル設定を確認してください。 
3. 機能
3.1. Top画面の作成
- 
レイアウトの利用 
 Top画面の構成は下図のようになっています。画面共通で利用する2つのレイアウト用テンプレートdefaultLayoutとshippingLayoutを用意し、画面のメインエリアは機能に併せて呼びだす形にしています。ここでは画面のメインエリアにtopテンプレートを利用しています。 ファイル src/main/webapp/jsp/samples/ec01/layout/defaultLayout.jsp <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ page import="org.iplass.mtp.web.template.TemplateUtil"%> <%@ page import="samples.ec01.utils.URLHelper" %> <%@ taglib prefix="m" uri="http://iplass.org/tags/mtp"%> (1) <%@ taglib prefix="c" uri="jakarta.tags.core"%> ----------------------------------------以上略---------------------------------------- <!-- 小さい画面で表示 end--> <div class="row layout-container"> <div class="col-md-3 d-none d-md-block"> <div class="row"> <div class="col-12"> <div class="list-group list-group-flush"> <a href="${m:tcPath()}/samples/ec01/top" class="list-group-item list-group-item-action fw-bold border-top">${m:rs('iplass-wtp-messages', 'samples.ec01.layout.defaultLayout.home')}</a> <c:forEach var="category" items="${categoryList}"> <a href="${URLHelper.getCategoryPath(category.oid)}" class="list-group-item list-group-item-action">${category.name}</a> </c:forEach> </div> </div> </div> </div> <div class="col-sm-12 col-md-9"> <m:renderContent /> (2) </div> </div> <hr> <div class="row"> <div class="col-12"> <ul class="list-group"> <li class="list-group-item border-0 fw-bold">Links</li> <li class="list-group-item border-0"> <a href="${m:tcPath()}/samples/ec01/news/newInfo" class="text-dark">${m:rs('iplass-wtp-messages','samples.ec01.layout.defaultLayout.news')}</a> </li> <li class="list-group-item border-0"> <a href="${m:tcPath()}/samples/ec01/search/search" class="text-dark">${m:rs('iplass-wtp-messages','samples.ec01.layout.defaultLayout.search')}</a> </li> <li class="list-group-item border-0"> <a href="${m:tcPath()}/samples/ec01/inquiry/inquiry" class="text-dark">${m:rs('iplass-wtp-messages','samples.ec01.layout.defaultLayout.inquiry')}</a> </li> <li class="list-group-item border-0"> <a href="${m:tcPath()}/samples/ec01/shop/tradeLaw" class="text-dark">${m:rs('iplass-wtp-messages','samples.ec01.layout.defaultLayout.SCTAInfo')}</a> </li> </ul> </div> </div> ----------------------------------------以下略----------------------------------------1 iPLAssのJSPタグライブラリ 2 LayoutテンプレートのJSPにおいて、メインコンテンツを表示する箇所を指定するJSPタグです。 
- 
メタデータ定義ファイル ファイル名 samples-ec01-ce-metadata.xml <contextPath name="/template"> ----------------------------------------以上略---------------------------------------- <!-- レイアウト --> <metaDataEntry> <metaData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="metaJspTemplate"> <name>samples/ec01/layout/defaultLayout</name> <displayName>標準レイアウト</displayName> <path>/jsp/samples/ec01/layout/defaultLayout.jsp</path> (1) <contentType>text/html; charset=utf-8</contentType> </metaData> </metaDataEntry> ...... <metaDataEntry> <metaData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="metaJspTemplate"> <name>samples/ec01/top</name> <displayName>Top画面</displayName> <path>/jsp/samples/ec01/top.jsp</path> (2) <layoutId>/action/samples/ec01/layout/defaultLayout</layoutId> (3) <contentType>text/html; charset=utf-8</contentType> </metaData> </metaDataEntry> ----------------------------------------以下略----------------------------------------1 レイアウト用のテンプレートファイルを指定しています。 2 Top画面用のテンプレートファイルを指定しています。 3 Top画面のテンプレートでは、 layoutIdにレイアウト用テンプレートを指定しています。
3.2. Bean/Bean Validation
iPLAssのBean/Bean Validation機能を利用する場合、バリデーションチェックしたいBeanクラスのプロパティにアノテーションをつけることで、バリデーションが自動で実行されます。また、バリデーション結果を画面に表示できます。
サンプルアプリの問い合わせ登録画面で入力された値に対してバリデーションが実行される機能を例として説明していきます。
- 
必要なJavaクラスを作成します。 
 プロジェクトのフォルダsrc/main/java/samples/ec01/beanを開きます。src.main.java ┗ samples.ec01 ┣ bean ┗ annotation (1) ┃ ┣ Kana.java ┃ ┗ ...... ┣ ui ┣ validator (2) ┃ ┣ group (3) ┃ ┃ ┗ JapaneseChecks.java ┃ ┣ KanaValidator.java ┃ ┗ ...... ┣━ UserBean.java (4) ┗ ......1 カスタムのバリデーション 
 カスタムのバリデーションをsamples.ec01.bean.annotation.Kanaに記述します。カスタムのバリデーターで利用されています。2 カスタムのバリデーター 
 カスタムのバリデーターをsamples.ec01.bean.validator.KanaValidatorに記述します。カスタムのバリデーションで利用されています。3 カスタムのバリデーショングループ 
 カスタムのバリデーショングループをsamples.ec01.bean.validator.group.JapaneseChecksに記述します。Beanクラスのアノテーションで利用されています。4 JavaBeanクラス 
 samples.ec01.bean.UserBeanでカスタムのバリデーションとバリデーショングループを利用しています。
- 
バリデーションエラーメッセージを定義する 
 Bean Validationエラーメッセージはプロパティファイルに定義し、多言語利用が可能です。
- 
コマンドクラスでBean Validationを利用する ファイル名 /src/main/java/samples/ec01/command/inquiry/RegistInquiryCommand.java ...... @ActionMapping( name = "samples/ec01/inquiry/doInquiry", displayName = "お問合せ登録", privileged = true, tokenCheck = @TokenCheck( executeCheck = true, consume = true, exceptionRollback = true), result = { @Result( exception = MappingException.class, (1) type = Type.TEMPLATE, value = "samples/ec01/inquiry/inquiry"), @Result( status = Constants.CMD_EXEC_SUCCESS, type = Type.TEMPLATE, value = "samples/ec01/inquiry/inquirySuccess")}) @CommandClass( name = "samples/ec01/inquiry/RegistInquiryCommand", displayName = "お問合せ登録コマンド") public class RegistInquiryCommand implements Command { private final BeanParamMapper mapper = new BeanParamMapper().withValidation() .whitelistPropertyNameRegex("^(mail|content|familyName(Kana)?|firstName(Kana)?)$"); (2) public static final String RESULT_INQUIRY_BEAN = "inquiryBean"; @Override public String execute(RequestContext request) { // 入力チェック InquiryBean inquiryBean = new InquiryBean(); request.setAttribute(RESULT_INQUIRY_BEAN, inquiryBean); // 日本語専用"name_kana"取得フォーム if (Consts.LANGUAGE_JA.equals(TemplateUtil.getLanguage()) || TemplateUtil.getLanguage() == null) { mapper.populate(inquiryBean, request.getParamMap(), Default.class, JapaneseChecks.class); (3) } else { mapper.populate(inquiryBean, request.getParamMap(), Default.class); (4) } Inquiry inquiry = inquiryBean.toEntity(); // 問い合わせステータス // 1 : 未対応 // 2 : 対応中 // 3 : 対応完了 // 4 : 終了 SelectValue inquiryStatus = new SelectValue(InquiryStatus.NOT_DEAL.getValue()); inquiry.setInquiryStatus(inquiryStatus); // 請求の登録 EntityDaoHelper.insert(inquiry); return Constants.CMD_EXEC_SUCCESS; } }1 MappingExceptionが発生した場合の遷移先Templateを指定します。 
 ※ RequestスコープにWebRequestConstants.EXCEPTIONをキーにMappingExceptionが格納されます。2 Beanクラスに画面からの入力値をマッピングするためのユーティリティクラスを初期化します。 
 ※ whitelistPropertyNameRegexメソッドにセット可能な項目の正規表現式を指定します。3 多言語利用で「日本語」が選択されている、または多言語利用の設定が取得できなかった場合、 samples.ec01.bean.validator.group.JapaneseChecksグループとデフォルトグループに属する項目に対してバリデーションが実行されます。
 ※ populateメソッドはスレッドセーフですが、それ以外の delimitersなどの設定用メソッドはスレッドセーフではありません。 詳しい説明は、org.iplass.mtp.command.beanmapper.BeanParamMapperのJavaDocを参照してください。4 多言語利用で「日本語」以外が選択されている場合、デフォルトグループに属する項目のみに対してバリデーションが実行されます。 
- 
JSPテンプレートファイル ファイル名 /jsp/samples/ec01/inquiry/registInquiry.jsp ----------------------------------------以上略---------------------------------------- <m:bind bean="${inquiryBean}"> (1) <form class="custom-form mt-3" action="${m:tcPath()}/samples/ec01/inquiry/doInquiry" method="post"> <input type="hidden" name="_t" value="${m:token()}"> <div class="form-group row"> ...... <div class="col-12 col-md-6 mt-3"> <div> <m:bind prop="familyNameKana"> (2) <label for="${name}" class="col-form-label label-hidden">${m:rs('iplass-wtp-messages', 'samples.ec01.inquiry.regist.familyNameKana')}</label> <input type="text" class="form-control border rounded input-hint-visible" name="${name}" value="${value}" placeholder="${m:rs('iplass-wtp-messages', 'samples.ec01.inquiry.regist.familyNameKana')}"> (3) <small class="form-text text-danger"><m:errors /></small> (4) </m:bind> </div> </div> ...... </form> </m:bind> ----------------------------------------以下略----------------------------------------1 PageContextにBeanインスタンスをバインドします。2 PageContextにBeanインスタンスに格納されているプロパティ名と値をバインドします。autoDetectErrors=true(デフォルトがtrue)の場合、WebRequestConstants.EXCEPTIONをキーにMappingExceptionを取得し、MappingResultのインスタンスが自動解決されます。当該Bean、プロパティに紐付くエラーがバインドされます。3 バインドされたプロパティの名前と値をテキストボックスにバインドします。 4 バインドされたエラーメッセージを画面に出力します。 ※ 詳しい設定方法については、 org.iplass.mtp.web.template.tags.BindTagとorg.iplass.mtp.web.template.tags.ErrorsTagのJavaDocを参照してください。
- 
動作確認 - 
「姓」と「名」を空文字として登録しようとした場合、バリデーションエラーが発生することを画面から確認できます。 
- 
「セイ」と「メイ」に全角カタカナ以外の値を入れて登録しようとした場合、バリデーションエラーが発生することを画面から確認できます。  
- 
多言語利用で「英語」が選択された場合、英語のバリデーションエラーメッセージが表示されることを確認できます。  ※ 英語用の画面には、カタカナの「セイ」と「メイ」の入力項目がないため、日本語用の画面と比べてレイアウトに少し違いがあります。 
 
- 
3.3. Entity作成
- 
Admin ConsoleのEntity定義で Mapping Class機能を利用することで、Entity定義に合ったJavaクラスを自動生成することができます。パッケージ samples.ec01.entity 
- 
Entityの多言語設定をします。 
 Admin Console部分の設定でEntity多言語対応を参照してください。
3.4. ErrorUrlSelector
エラー画面Template制御スクリプトです。
- 
トークンエラーが発生した場合を例として説明していきます。 ファイル名 src/main/java/samples/ec01/web/EcErrorUrlSelector.java public class EcErrorUrlSelector extends GemErrorUrlSelector { private final String ROOT_PATH = "samples/ec01"; private final String TEMPURL_TOKEN_ERROR = ROOT_PATH + "/error/tokenError"; private final String TEMPURL_ENTITY_ERROR = ROOT_PATH + "/error/entityError"; private final String TEMPURL_USER_EXISTS_ERROR = ROOT_PATH + "/error/userExistsError"; private final String TEMPURL_SESSION_VALUE_NOT_FOUND_ERROR = ROOT_PATH + "/error/sessionValueNotFoundError"; private final String TEMPURL_SYSTEM_ERROR = ROOT_PATH + "/error/systemError"; @Override public String getErrorTemplateName(Throwable exception, RequestContext request, String path) { Exception error = (Exception) request.getAttribute(WebRequestConstants.EXCEPTION); // ECサイト用 if (path != null && path.startsWith(ROOT_PATH)) { // TokenValidationException用 if (error instanceof TokenValidationException) { return TEMPURL_TOKEN_ERROR; (1) // EntityValidationException用 } else if (error instanceof EntityValidationException) { return TEMPURL_ENTITY_ERROR; // UserExistsException } else if (error instanceof UserExistsException) { return TEMPURL_USER_EXISTS_ERROR; // SessionValueNotFoundException } else if (error instanceof SessionValueNotFoundException) { return TEMPURL_SESSION_VALUE_NOT_FOUND_ERROR; // その他のエラー用 } else { return TEMPURL_SYSTEM_ERROR; } } return super.getErrorTemplateName(exception, request, path); } }1 TokenValidationExceptionが発生した場合、カスタムの samples/ec01/error/tokenErrorテンプレートが呼び出されます。
- 
カスタムのErrorUrlSelectorの実装クラスを mtp-service-config.xmlに登録する必要があります。設定ファイルを参照ください。
- 
メタデータ定義ファイル ファイル名 samples-ec01-ce-metadata.xml <contextPath name="/template"> ----------------------------------------以上略---------------------------------------- <!-- エラー画面 --> <metaDataEntry> <metaData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="metaJspTemplate"> <name>samples/ec01/error/tokenError</name> (1) <displayName>トークンエラー画面</displayName> <path>/jsp/samples/ec01/error/genericError.jsp</path> (2) <layoutId>/action/samples/ec01/layout/defaultLayout</layoutId> <contentType>text/html; charset=utf-8</contentType> </metaData> </metaDataEntry> ----------------------------------------以下略----------------------------------------1 トークンエラー画面のテンプレート名 2 エラーテンプレートファイルを指定 
- 
共通エラーテンプレートファイル ファイル名 /src/main/webapp/jsp/samples/ec01/error/genericError.jsp 
- 
動作確認 - 
TokenValidationExceptionが発生した場合、カスタムのテンプレートが表示されること。 コンソールに出力されたエラーログは以下の通りです。 org.iplass.mtp.web.actionmapping.TokenValidationException: 不正な画面遷移が発生しました(一連の登録処理中にブラウザの戻るボタン等を押下してしまいますと正常に処理を継続できない場合があります)。 at org.iplass.mtp.impl.web.interceptors.TokenInterceptor.doError(TokenInterceptor.java:84) at org.iplass.mtp.impl.web.interceptors.TokenInterceptor.intercept(TokenInterceptor.java:114) at org.iplass.mtp.impl.command.InvocationImpl.proceedCommand(InvocationImpl.java:115) at org.iplass.mtp.impl.web.actionmapping.WebInvocationImpl.proceedCommand(WebInvocationImpl.java:171) at org.iplass.mtp.impl.command.interceptors.TransactionInterceptor.lambda$intercept$0(TransactionInterceptor.java:34) at org.iplass.mtp.transaction.TransactionManager.doTransaction(TransactionManager.java:114) at org.iplass.mtp.transaction.Transaction.with(Transaction.java:303) at org.iplass.mtp.impl.command.interceptors.TransactionInterceptor.intercept(TransactionInterceptor.java:33)
- 
エラー内容表示  
 
- 
3.5. ReportOutput(注文明細ダウンロード)
- 
POIを用いてExcel形式の注文明細帳票の出力機能を実装しています。 
- 
Java/JSP版では、Javaクラスを用いてTemplateの「Output Logic」を実装しています。 ファイル名 samples.ec01.template.report.OrderOutputLogic 
- 
帳票テンプレート 
 作成方法は、開発者ガイドの帳票出力(Jasper/JXLS/POI)の章を参照ください。
- 
POI帳票出力機能の動作確認 - 
管理画面で注文検索一覧画面を開き、「詳細」リンクをクリックします。  
- 
「注文情報ダウンロード」ボタンをクリックします。  
- 
ダウンロードしたExcel帳票を確認します。 
 サンプルアプリでは、英語用の帳票テンプレートも別途用意しています。 
 
- 
3.6. Buildin EL関数
- 
iPLAssのJSPタグライブラリに定義されているビルトインのEL関数を利用しています。 <%@ taglib prefix="m" uri="http://iplass.org/tags/mtp"%> (1) ----------------------------------------以上略---------------------------------------- <div class="row"> <div class="col-12"> <div class="border-top"></div> <nav class="breadcrumb all-breadcrumb"> <a class="breadcrumb-item text-primary" href="${m:tcPath()}/samples/ec01/top"> (2) ${m:rs('iplass-wtp-messages', 'samples.ec01.all.breadcrumb.home')} (3) </a> <span class="breadcrumb-item active"> ${m:rs('iplass-wtp-messages', 'samples.ec01.inquiry.regist.title')} </span> </nav> </div> <div class="col-12"> <span class="h4">${m:rs('iplass-wtp-messages', 'samples.ec01.inquiry.regist.title')}</span> <m:bind bean="${inquiryBean}"> <form class="custom-form mt-3" action="${m:tcPath()}/samples/ec01/inquiry/doInquiry" method="post"> <input type="hidden" name="_t" value="${m:token()}"> (4) <div class="form-group row"> <div class="col-12 col-md-6 mt-3"> <div> <m:bind prop="familyName"> <label for="${name}" class="col-form-label label-hidden">${m:rs('iplass-wtp-messages', 'samples.ec01.inquiry.regist.familyName')}</label> <input type="text" class="form-control border rounded input-hint-visible" name="${name}" value="${value}" placeholder="${m:rs('iplass-wtp-messages', 'samples.ec01.inquiry.regist.familyName')}"> <small class="form-text text-danger"><m:errors /></small> </m:bind> </div> </div> ----------------------------------------以下略----------------------------------------1 iPLAssのJSPタグライブラリ 2 tcPath(): テナント名まで含むコンテキストパスを取得します。 3 rs(): 指定された基底名とキーからResourceBundleに定義された文字列を返します。多言語メッセージを併せて利用できます。 4 token(): トランザクショントークンを出力します。 ※ 他のEL関数の使い方については、 org.iplass.mtp.web.template.ELFunctionsのJavaDocを参照してください。
3.7. WebApiとの連携
Ajaxを利用することで、WebApiで連携することができます。一般消費者向けのECサイト画面における全文検索処理を例として説明していきます。
- 
開発者ガイドのWebApiの作成方法を参照してください。 
 以下はここで利用しているWebApiです。WebApi名 samples/ec01/search/fulltextSearch 
- 
WebApiを利用しているJSPテンプレート ファイル名 /src/main/webapp/jsp/samples/ec01/search/search.jsp ----------------------------------------JSP部分略---------------------------------------- <script type="text/javascript"> function fullTextSearch() { const productName = $("#productName").val(); const categoryOid = $("#categoryList").attr("category-item-selected"); if(productName == "") { $("#helpId").html("${m:rs('iplass-wtp-messages', 'samples.ec01.search.nokeyword')}"); return false; } const param = "{\"productName\":\"" + productName + "\",\"categoryOid\":\"" + categoryOid +"\"}"; $.ajax({ type: "POST", contentType: "application/json", url:"${m:tcPath()}/api/samples/ec01/search/fulltextSearch", (1) dataType: "json", data: param, success: function(commandResult){ if (commandResult.exceptionType != null) { (2) alert("${m:rs('iplass-wtp-messages', 'samples.ec01.search.jsError')}"+ commandResult.exceptionType +"\\n"+commandResult.exceptionMessage); return; } if(commandResult.status == "SUCCESS"){ if(commandResult.defaultResult != null && commandResult.defaultResult.length > 0){ const resultHtml = ListSearchResult(commandResult.defaultResult, productName); (3) $("#searchResultDiv").html(resultHtml); } else{ $("#helpId").html("${m:rs('iplass-wtp-messages', 'samples.ec01.search.keyword')}: " + productName + ", " + "${m:rs('iplass-wtp-messages', 'samples.ec01.search.noResult')}"); } } } }); } function dropdownSelect(item){ const t = $(item); const v = t.attr("category-item-value"); $("#categoryList").text(t.html()).attr("category-item-selected", v); } function ListSearchResult(entities, productName){ (4) const yen = "${m:rs('iplass-wtp-messages', 'samples.ec01.all.yen')}"; let html = "<div class=\"col-12 mb-2\">"; html += " <h4>${m:rs('iplass-wtp-messages', 'samples.ec01.search.result')}" + productName + "</h4>"; html += "</div>"; for(let i =0; i < entities.length; i++){ const name = entities[i].name; const price = isNaN(entities[i].price)? "" : entities[i].price; const imageUrl = "${m:tcPath()}/samples/ec01/resource/bin?id=" + entities[i].productImg.lobId + "&type=${Consts.BIN_TYPE_PRODUCT_IMG}"; const detailUrl = "${m:tcPath()}/samples/ec01/product/detail?productId=" + entities[i].oid; html += "<div class=\"col-12 col-md-4\">"; html += " <div class=\"card border-light border-0\">"; html += " <a href=\"" + detailUrl + "\" class=\"h-100\">"; html += " <img class=\"card-img-top img-thumbnail img-fluid all-product-img\" src=" + imageUrl + " alt=\"" + name + "\">"; html += " </a>"; html += " <div class=\"card-body pt-md-1 text-center\">"; html += " <div>"; html += " <a href=\""+ detailUrl +"\" class=\"card-link text-dark\">" + name + "</a>"; html += " </div>"; html += " <div class=\"all-price\">"; html += " <span>" + price + "</span>" + yen; html += " </div>"; html += " </div>"; html += " </div>"; html += "</div>"; } return html; } </script>1 WebApiによる検索処理を呼び出します。 2 検索処理でエラーが発生した場合、クライアント側での処理。 3 返された検索処理の結果を取得し、クライアント側の描画処理を呼び出します。 4 検索結果の描画処理。 
- 
動作確認 - 
キーワードを入力し、検索ボタンをクリックします。  
- 
返却値の例 {"status":"SUCCESS","defaultResult":[{"definitionName":"samples.ec01.products.Product","price":2709,"name":"Javaルールブック ~読みやすく効率的なコードの原則","productImg":{"lobId":195,"name":"wolf.png","type":"image/png","definitionName":"samples.ec01.products.Product","propertyName":"productImg","oid":"EC011","size":13872},"oid":"EC011"},{"definitionName":"samples.ec01.products.Product","price":2300,"name":"超図解 Javaルールブック (超図解シリーズ) [単行本]","productImg":{"lobId":208,"name":"bear.png","type":"image/png","definitionName":"samples.ec01.products.Product","propertyName":"productImg","oid":"EC019","size":12051},"oid":"EC019"}]}
- 
検索結果を画面から確認できます。  
 
- 
3.8. セキュリティ対策
オレンジ色の部分を例にセキュリティ対策を解説していきます。
 
ここでは会員登録を例としてiPLAssで利用できる以下のセキュリティ対策について説明します。
・ XSS対策となるエスケープ機能 : ユーザーの入力内容を正常に画面表示させる
・ CSRF対策/トランザクション重複起動対策となるトークンチェック機能 : 画面表示時に正常な画面遷移が行われているかをチェックする
会員登録処理の中でそれぞれの機能は下記のように利用されます。
- 
エスケープの導入 
入力された値を出力する会員情報確認画面にて、該当箇所に ${m:esc()} エスケープを導入します。
| 表示名 | Template名 | 
|---|---|
| 会員情報確認画面 | samples/ec01/member/confirmMemberInfo | 
<%@ taglib prefix="m" uri="http://iplass.org/tags/mtp"%> (1)
----------------------------------------以上略----------------------------------------
        <div class="card col-12 bg-light">
            <div class="card-body">
                <div class="row mt-3 border-bottom">
                    <div class="col-12 col-md-4">
                        <span class="text-muted fw-bold">${m:rs('iplass-wtp-messages', 'samples.ec01.member.regist.userId')}</span>
                    </div>
                    <div class="col-12 col-md-8">${m:esc(userBean.userId)}</div>
                </div>
                <div class="row mt-3 border-bottom">
                    <div class="col-12 col-md-4">
                        <span class="text-muted  fw-bold">${m:rs('iplass-wtp-messages', 'samples.ec01.member.registConfirm.fullName')}</span>
                    </div>
                    <div class="col-12 col-md-4">
                        <span class="text-muted  fw-bold">${m:rs('iplass-wtp-messages', 'samples.ec01.member.regist.familyName')}</span>
                         ${m:esc(userBean.familyName)} (2)
                    </div>
                    <div class="col-12 col-md-4">
                        <span class="text-muted  fw-bold">${m:rs('iplass-wtp-messages', 'samples.ec01.member.regist.firstName')}</span>
                         ${m:esc(userBean.firstName)} (2)
                    </div>
                </div>
                <% if (Consts.LANGUAGE_JA.equals(TemplateUtil.getLanguage()) || TemplateUtil.getLanguage() == null) { %>
                <div class="row mt-3 border-bottom">
                    <div class="col-12 col-md-4">
                        <span class="text-muted  fw-bold">${m:rs('iplass-wtp-messages', 'samples.ec01.member.registConfirm.fullNameKana')}</span>
                    </div>
                    <div class="col-12 col-md-4">
                        <span class="text-muted  fw-bold">${m:rs('iplass-wtp-messages', 'samples.ec01.member.regist.familyNameKana')}</span>
                         ${m:esc(userBean.familyNameKana)} (2)
                    </div>
                    <div class="col-12 col-md-4">
                        <span class="text-muted  fw-bold">${m:rs('iplass-wtp-messages', 'samples.ec01.member.regist.firstNameKana')}</span>
                         ${m:esc(userBean.firstNameKana)} (2)
                    </div>
                </div>
                <% } %>
                <div class="row mt-3 border-bottom">
                    <div class="col-12 col-md-4">
                        <span class="text-muted  fw-bold">${m:rs('iplass-wtp-messages', 'samples.ec01.member.regist.mail')}</span>
                    </div>
                    <div class="col-12 col-md-8">${m:esc(userBean.mail)}</div> (2)
                </div>
            </div>
        </div>
----------------------------------------以下略----------------------------------------| 1 | iPLAssのJSPタグライブラリ | ||||||||||||
| 2 | 今回は出力先がHTML形式であるためすべて ${m:esc(変数名)}の形式でエスケープを行っています。
 | ||||||||||||
| 3 | ActionMapping定義にTokenCheckのアノテーションを利用してトークンチェックを行う設定を追加します。 以下の設定が可能です。 
 この設定により直接この画面へ遷移するとエラーページが表示されます。 
 
 | ||||||||||||
| 4 | EL関数を利用してトークンを生成します。 | 
3.9. 多言語対応
多言語を利用するために、サンプルアプリでは以下の対応を行っています。
- 
言語別にメッセージプロパティファイルを作成 
- 
言語別にBean Validationエラーメッセージのプロパティファイルを作成 
4. 設定ファイル
4.1. service-config
| ファイル名 | /src/main/resources/mtp-service-config.xml | 
|---|
- 
カスタムのErrorUrlSelectorの登録 <!-- Web App Settings --> <service> <interface>org.iplass.mtp.impl.web.WebFrontendService</interface> <!-- ■ your error url selector ■ --> <property name="errorUrlSelector" class="samples.ec01.web.EcErrorUrlSelector" /> (1) </service>1 カスタムのErrorUrlSelectorを登録しています。 
- 
メタデータ定義の登録 <!-- XmlResource MetaData and Annotation MetaData Settings --> <service> <interface>org.iplass.mtp.impl.metadata.MetaDataRepository</interface> <!-- ■ your app metadata xml file name (additional="true) ■ --> <property name="resourcePath" value="/samples-ec01-ce-metadata.xml" additional="true" /> (1) <!-- ■ your app command list class (additional="true) ■ --> <property name="annotatedClass" value="samples.ec01.command.metadata.CommandList" additional="true"/> (2) </service>1 メタデータの定義ファイル 2 アノテーションでコマンドのメタデータ定義を作っています。 
- 
全文検索機能 <!-- Fulltext Search Service Settings --> <service> <interface>org.iplass.mtp.impl.fulltextsearch.FulltextSearchService</interface> <property name="useFulltextSearch" value="true" /> (1) <property name="maxRows" value="1000" /> <property name="throwExceptionWhenOverLimit" value="true"/> <!-- lucene利用 --> <class>org.iplass.mtp.impl.fulltextsearch.lucene.LuceneFulltextSearchService</class> <property name="directory" value="[set your lucene index file store path. eg: D:\tmp\lucene]" /> (2) <property name="analyzer" value="org.apache.lucene.analysis.ja.JapaneseAnalyzer" /> <property name="indexWriterRAMBufferSizeMB" value="64.0"/> <property name="redundantTimeMinutes" value="10"/> </service>1 全文検索機能を有効化しています。 2 全文検索のINDEXファイルの作成場所を指定してください。 
5. リソースファイル
5.1. メタデータ定義
- 
コマンドのメタデータ定義 ファイル名 /src/main/java/samples/ec01/command/metadata/CommandList.java 
- 
他のメタデータ定義 ファイル名 /src/main/resources/samples-ec01-ce-metadata.xml ここでは2種類のメタデータが用意されています。 metaDataList ┣ contextPath (name=/entity) (1) ┣ contextPath (name=/property/select) (1) ┣ contextPath (name=/template) ※ 帳票出力機能と管理トップ画面 (1) ┣ contextPath (name=/view/calendar) (1) ┣ contextPath (name=/view/generic) (1) ┣ contextPath (name=/view/menu/item) (1) ┣ contextPath (name=/view/menu/tree) (1) ┣ contextPath (name=/view/top) (1) ┣ contextPath (name=/view/treeview) (1) ┣ contextPath (name=/view/filter) (1) ┗ contextPath (name=/template) (2)1 Admin Consoleで作成したメタデータ定義です。Packaging機能もしくはMetaDataExplorer機能を利用することでエクスポートしたものです。 2 Templateのメタデータ定義は手動で作成しています。 
 ※ 帳票出力機能と管理トップ画面のTemplate定義はAdmin Consoleで作成し、エクスポートしています。手動で作成したTemplate定義の例。 ----------------------------------------以上略---------------------------------------- <contextPath name="/template"> <!-- エラー画面 --> <metaDataEntry> <metaData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="metaJspTemplate"> <name>samples/ec01/error/loginError</name> <displayName>認証エラー画面</displayName> <path>/jsp/samples/ec01/error/genericError.jsp</path> <layoutId>/action/samples/ec01/layout/defaultLayout</layoutId> <contentType>text/html; charset=utf-8</contentType> </metaData> </metaDataEntry> ----------------------------------------以下略----------------------------------------
5.2. メッセージファイル
- 
JSPテンプレートで特定のResourceBundleに定義された文字列を取得することができ、言語別にプロパティファイルを作成することで多言語利用が可能です。 ファイル名 iplass-wtp-messages_ja.properties samples.ec01.all.breadcrumb.home = ホーム samples.ec01.all.pagination.next = 次へ samples.ec01.all.pagination.prev = 前へ samples.ec01.all.yen = 円 samples.ec01.backoffice.stockUpdate.doUpdate = 一括更新 samples.ec01.backoffice.stockUpdate.product = 商品名 samples.ec01.backoffice.stockUpdate.stock = 在庫数 samples.ec01.backoffice.stockUpdate.title = 在庫一括更新 samples.ec01.cart.info.checkOut = お会計 samples.ec01.cart.info.emptyMsg.p1 = カートが空になっています。 ----------------------------------------以下略----------------------------------------ファイル名 iplass-wtp-messages_en.properties samples.ec01.all.breadcrumb.home = Home samples.ec01.all.pagination.next = Next samples.ec01.all.pagination.prev = Prev samples.ec01.all.yen = Yen samples.ec01.backoffice.stockUpdate.doUpdate = Bulk update samples.ec01.backoffice.stockUpdate.product = Product name samples.ec01.backoffice.stockUpdate.stock = Stock samples.ec01.backoffice.stockUpdate.title = Stock bulk update samples.ec01.cart.info.checkOut = Check out samples.ec01.cart.info.emptyMsg.p1 = Your cart is currently empty. ----------------------------------------以下略----------------------------------------
5.3. Bean Validationエラーメッセージ
- 
言語別にプロパティファイルを作成することで多言語利用が可能です。 ファイル名 /src/main/resources/ValidationMessages_ja.properties samples.ec01.bean.validation.Tel.invalidChar = 「数字」および「-」のみ利用可能です。(1) samples.ec01.bean.validation.UserId.invalidChar = 「英数字」および「-」(ハイフン)「@」「_」「.」(ピリオド)のみ利用可能です。 samples.ec01.bean.validation.UserId.outOfLength = ユーザーIDは{min}文字以上{max}文字以下です (2) samples.ec01.bean.validation.isBlank = 値を入力してください。 samples.ec01.bean.validation.notKana = 全角カタカナを入力してください。 ......ファイル名 /src/main/resources/ValidationMessages_en.properties samples.ec01.bean.validation.Tel.invalidChar = Please enter alphanumeric characters or "-"(hypen) . (1) samples.ec01.bean.validation.UserId.invalidChar = Please enter alphanumeric characters or "-"(hypen), "@", "_"(underscore), "."(dot) . samples.ec01.bean.validation.UserId.outOfLength = User ID must be in range between {min} and {max} characters. (2) samples.ec01.bean.validation.isBlank = Please enter value. samples.ec01.bean.validation.notKana = Please enter Katakana. ......1 多言語を利用するので、日本語と英語のバリデーションエラーメッセージを用意しています。 2 メッセージにパラメーターの利用が可能です。 
6. テストコード
- 
プロジェクトの src/test/フォルダを開くと、以下の構成になっています。src.test ┣ java ┃ ┗ samples.ec01.test.command (1) ┃ ┣ inquiry ┃ ┃ ┗ RegistInquiryCommandTest.java ┃ ┗ TopCommandTest.java ┗ resources (2) ┣ mtptest.properties ┗ test-mtp-service-config.xml1 テストクラス 2 テストクラス実行用の設定ファイル 
- 
テストクラス パッケージ クラス名 samples.ec01.test.command TopCommand.java samples.ec01.test.command.inquiry RegistInquiryCommandTest.java 
- 
テスト実行用の設定ファイル ファイル名 /src/test/resources/mtptest.properties configFileName=/test-mtp-service-config.xml (1) rollbackTransaction=true (2) tenantName= (3) userId= (4) password= (5)1 テスト実行用のデータベース接続とメタデータ定義が記述されたファイル 2 トランザクションロールバックの設定 3 テストを実行するテナント 4 テストを実行するユーザー 5 テストを実行するユーザーのパスワード ※ プロパティによる指定以外に、クラスレベルアノテーションとメソッドレベルアノテーションによる指定も可能です。詳しい設定方法については、 org.iplass.mtp.test.MTPJUnitTestRuleのJavaDocを参照ください。ファイル名 /src/test/resources/test-mtp-service-config.xml <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE serviceDefinition> <serviceDefinition> <!-- If use oracle, inherits mtp-service-config-mysql.xml for convenience. --> <!-- <inherits>/mtp-core-service-config-oracle.xml</inherits> --> <inherits>/mtp-web-service-config.xml</inherits> <inherits>/gem-service-config.xml</inherits> <inherits>/mtp-core-service-config-mysql.xml</inherits> <!-- Rdb Connection Settings --> <service> <interface>org.iplass.mtp.impl.rdb.connection.ConnectionFactory</interface> <class>org.iplass.mtp.impl.rdb.connection.DriverManagerConnectionFactory</class> <property name="url" value="jdbc:mysql://[host]:[port]/[schema]" /> (1) <property name="user" value="[user]" /> (2) <property name="password" value="[password]" /> (3) <property name="driver" value="com.mysql.cj.jdbc.Driver" /> </service> <!-- XmlResource MetaData and Annotation MetaData Settings --> <service> <interface>org.iplass.mtp.impl.metadata.MetaDataRepository</interface> <property name="resourcePath" value="/samples-ec01-ce-metadata.xml" additional="true" /> (4) <property name="annotatedClass" value="samples.ec01.command.metadata.CommandList" additional="true"/> (5) </service> </serviceDefinition>1 テスト実行用のデータベース接続 2 データベース接続ユーザー 3 データベース接続ユーザーのパスワード 4 メタデータの定義ファイル 5 コマンドのメタデータ定義 
- 
動作確認 - 
Gradleでテストコードを実行する場合、プロジェクトのルートパスで以下のコマンドを実行してください。 gradlew test
- 
実行結果 :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :compileTestJava UP-TO-DATE :processTestResources UP-TO-DATE :testClasses UP-TO-DATE :test UP-TO-DATE BUILD SUCCESSFUL Total time: 1.855 secs
- 
Eclipseでテストコードを実行する場合、 src.test.javaパッケージを右クリックしてRun asを選択し、JUnit Testを実行してください。
 
- 
