レガシーなTomcatアプリケーションを修正した
JSP(JavaServer Pages)も今じゃすっかりレガシー扱いで、新規案件でお目にかかることは大規模システムを除くと殆ど無くなった。
今回、古いWebアプリケーションの補修・改修案件で、十何年かぶりにJSPを触る機会があったので、基礎用語の解説も交えながら作業ログを残す。
はじめに
サーバ環境
Tomcatをマイクロソフト社のパブリッククラウドAzureで動かしている。サーバ構成は、前回の記事(アプリケーションサーバをごっそり載せ替えた話)を参照。StrutsやSpringなどのフレームワークは使っていなくて、JSPだけで構築されている。
作業上の制約
- あるのは本番サーバのみ。テストサーバは無い。
- Javaのソースコードも無い。あるのはwarファイルのみ。
- 当然、ドキュメントも無い。
改修内容
ある条件を満たしたときに、特定のボタンを表示しないようにするだけだ。
JavaのWebアプリケーション(J2EE)におけるMVCモデルは下表の通り。
レイヤー | J2EEでの呼び名 |
---|---|
Model層 | Bean |
View層 | JSP |
Controller層 | Servlet |
今回は表示を変えるだけなので、手を入れるのはJSPだけで良く、Javaのソースコードが無いことは大きな障壁とはならない。ただし「ある条件を満たすかどうか」はビジネスロジックの範疇となりBeanが管理しているため、Beanの解析は必要になる。
解析と修正の進め方
Beanって何だっけ?
Beanとは、教科書通りに説明するなら「JavaBeansやEJB(Enterprise JavaBeans)の仕様に則って作られた再利用可能なJavaプログラム」のことである。
簡単に言うと、プロパティ(属性)を持つただのJavaクラスだ。
JSPからBeanを呼び出すにはuseBean
というJSPタグを使う。このタグでBeanのインスタンスを生成、つまりBeanオブジェクトを作り、スクリプトレットから利用したり、setProperty
/getProperty
タグでBeanのプロパティにアクセスする。
<jsp:useBean id="[オブジェクト名]" class="[クラス名]"/>
Beanの解析には Java Decompiler を使った。クラスファイルやjarファイルから、Javaのソースコードを生成する逆コンパイラである。
解析を進めると、クラス名bean.ClientCheckinBean
で所望のメソッドが定義されていると分かる。
JSPの修正
クラス名が分かったので、該当のJSPファイルから、このクラスを呼び出している箇所を検索してみる。
1: <%@ page contentType = "text/html;charset=Shift_JIS" %>
2: <% response.setContentType("text/html;charset=Shift_JIS"); %>
3: <% request.setCharacterEncoding("Shift_JIS"); %>
4: <%@ include file = "Nocache.inc" %>
5: <%@ page import = "common.Sanitizer" %>
6: <%@ page import = "java.util.concurrent.CopyOnWriteArrayList" %>
7: <%@ page import = "java.util.Calendar" %>
8: <jsp:useBean id="CHECKIN_DATA" scope="session" class="bean.ClientCheckinBean" />
9: <jsp:useBean id="DATA" scope="session" class="java.util.concurrent.CopyOnWriteArrayList" />
8行目で CHECKIN_DATA
というsessionスコープのオブジェクトに入れていることが分かる。
試しに、このクラスのgetClientcd
メソッドの戻り値を「あぶりだし」で画面に表示してみる。
<font color="white"><!-- 文字の色を白にする -->
<%= CHECKIN_DATA.getClientcd() %>
</font>
この俗に言うあぶりだしとは、背景色と文字色を同じにすることで、そのままでは読めないがカーソルで選択し反転させると文字が浮かび上がる効果を利用したものだ。本番サーバなので、他人に気づかれずに動作を確認したい。
想定通りの値を確認できたので、条件を満たすとボタンを表示しないよう修正。
<input type="button" name="btn1" value=" 出勤 " onClick="checkinAction('1')">
<input type="button" name="btn2" value=" 退勤 " onClick="checkinAction('2')">
<% if (CHECKIN_DATA.getClientcd().intValue() == 123) { %> <!-- 追加 -->
<input type="button" name="btn3" value="公用外出" onClick="checkinAction('3')">
<input type="button" name="btn4" value="公用戻り" onClick="checkinAction('4')">
<% } %> <!-- 追加 -->
が! あるボタンのクリック時に発火されるJavaScriptのイベントでエラーになってしまった。
F12キーでChromeのデベロッパーツールを起動すると、次のエラーメッセージがコンソールに出力されている。
Uncaught TypeError: Cannot set property 'disabled' of undefined
at disableForm (ClientCheckinServlet:54)
at checkinAction (ClientCheckinServlet:41)
at HTMLInputElement.onclick (ClientCheckinServlet:329)
disableForm @ ClientCheckinServlet:54
checkinAction @ ClientCheckinServlet:41
onclick @ ClientCheckinServlet:329
JSPによってボタンを減らされたHTMLがブラウザに返却され、JavaScriptがそのボタンの要素を参照しようとして「未定義だ」とエラーになっている。
try
~catch
でエラーを無視しても良いのだが、ここは丁寧に、ボタンが存在するか判定してから要素を参照するようにした。
document.checkin.btn1.disabled = true;
document.checkin.btn2.disabled = true;
if (document.checkin.btn3) { // 追加
document.checkin.btn3.disabled = true;
document.checkin.btn4.disabled = true;
}
jQueryなら違う書き方もできるだろうが、なにせ古いWebアプリケーションなので生のJavaScriptで対応した。
Author And Source
この問題について(レガシーなTomcatアプリケーションを修正した), 我々は、より多くの情報をここで見つけました https://qiita.com/mindwood/items/d00e811f6ff48873dce4著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .