bokusui について

ソフトウェアハウスでのPG・SEから始まり、10年近く勤めた金融系企業の社内SEを数年前にやめ、フリーランス時代を経たのち法人成りしました。システム開発の全工程をこじんまりとやり続けています。

TeraTermマクロでvi編集

 マシンが増えたのでサブネットを多数のサーバで一気に変えなきゃという話があり、対象サーバを確認したところ、Windows系もあるけどCentOSの6系が多いとの事。サブネットを変えるにはどうしたらいいのかいろいろ調べたと所、CentOS6だと一時的なのはifconfigで行けるけど、恒久的な変更だとコンフィグファイル更新が必要らしい。ちなみにCentOS7だとまた別のコマンドらしい。windows系は調査中。。。
 という事でリモート端末から一気に複数のサーバを更新するという要件なので、とりあえずはTeraTermマクロを作ってそれを各サーバに繰り返すという形にします。
 下記例はログインや引数処理、該当ネットワーク名の取得等は割愛し、viコマンドによるファイル編集をマクロで実行する箇所をサブルーチンにし、それをコールする内容です。コンフィグファイルは/etc/syscomfigにある該当ネットワークのファイル(下記のCONFFILE)に指定し、検索文字列(下記のSEARCHSTR)はPREFIX=、サブネットをBit24にするとして上書文字列(下記のAPPENDSTR)に24をセットしてサブルーチンをコールしています。

CONFFILE='/etc/sysconfig/network-scripts/ifcfg-eth0'
SEARCHSTR='PREFIX='
APPENDSTR='24'
call CHANGECONF
end

:CHANGECONF
;コンフィグ変更サブルーチン 
; CONFFILE	コンフィグファイル名
; SEARCHSTR	検索文字列
; APPENDSTR	上書文字列

timeout= 3
CMD = 'vi '
strconcat CMD CONFFILE
sendln CMD

;/を送る
send '/'
pause 1
; 検索文字へ移動
sendln SEARCHSTR
pause 1
strlen SEARCHSTR
sendkcode 333 result

; Rで上書き
send 'R'

send APPENDSTR

; エスケープ
send #$1B

send ':'
pause 1
sendln 'wq!'
pause 1
 
; 終了
return

コマンド打った後にwaitlnとかでは出来ないのでpauseを入れてとりあえずは動く形になりましたが、環境によりタイミングは変わるのでエラーハンドリングを追加した方が良さそうです。

JSF2でbootstrapのdatetimepickerを使ってみる

 前回、画面の日付入力にdatepickerを使ってみましたが、
bootstrapのdatepickerを使ってみる
今度は時間の入力も必要な画面を追加する事になり、datetimepickerを使ってみる事にしました。
 使用したのは下記です。日本語も対応されています。
http://www.malot.fr/bootstrap-datetimepicker
 ダウンロードしたJSとCSSをdatepickerの時と同じようにコンテンツディレクトリ配下のresource内に配置。
resource

xhtmlは下記のようになりました。aInfoという管理BeanにdispFromDateとdispEndDateの2つの文字列日付プロパティがあり、ボタン押下でgetListを呼び出し結果をresultListにdataTableで表示するという内容です。

<script type="text/javascript">
$(function() {
	  $('.datetimepicker').datetimepicker({
		  format: 'yyyy/mm/dd hh:ii:ss',
	      autoclose: true,
	      todayBtn: true,
	      pickerPosition: "bottom-left",
		  language: 'ja'
	  });
	});
</script>
・・・中略

<h:inputText id="fromDate" value="#{aInfo.dispFromDate}" class="datetimepicker from-control span2" >
  <f:ajax  execute="@this"/>
</h:inputText>
<h:inputText id="toDate" value="#{aInfo.dispEndDate}" class="datetimepicker from-control span2" >
  <f:ajax  execute="@this"/>
</h:inputText>
・・・中略

<h:commandButton id="listget" action="#{aInfo.getList}" value="表示" class="btn btn-default safebutton">
  <f:ajax execute="fromDate toDate" render="resultList"/>
</h:commandButton>

・・・中略
<h:dataTable id="resultList" var="list" value="#{aInfo.resultList}" class="table table-striped" >
・・・以下略

なお、日付フォーマットは今回の要件的に秒も対象にしていますが、datetimepickerのUI自体は分までが対応範囲なので秒については直入力という整理です。

以外と知られていないが便利なHTA

 Windows用で画面一つだけとかの簡単なツールを作る場合、HTA(HTML Applications)をよく使います。かなり昔からありますが、かなりマイナーな存在のようです。筆者もその存在に気が付いたのは実は3年くらい前です。。小さい規模でしか使う事はまずないので、HTAを使うような開発を外部へ発注するような事がなかったからでしょうか?
 基本的にIE上で動作しているので、MicrosoftがIEを辞めたら終わりかも知れませんが、テキストエディタのみで作れるし、VBScriptとJavaScriptの組み合わせで大抵の事は実現できます。特に助かるのが、企業内で使う端末って勝手にソフトをインストール出来なかったり、会社によっていろんな制約があったりしますが、HTAなら端末環境上の影響がなく、Windows端末でさえあればOKという点です。少数で使うちょっとした画面を作るという前提では結構便利だと思います。

Coherenceと1年付き合ってみた

 OracleのインメモリーグリッドCoherenceを使った開発に携わり1年立ちました。途中の数ヶ月は別の事もやったりで比較的まったりやっていましたが、この辺でちょっと整理。別に筆者が選定した訳ではありませんが、今携わっている仕事でCoherenceを使う事になったのは、Webサービスでミッションクリティカルかつ、応答レスポンスの最小化という要件を満たす為でした。構成はこんな感じです。

構成

 上記のWebLogicにデプロイされているフロントアプリケーションと、別途バックにJavaVMで動作させているプロセスの両方にCoherenceのキャッシュデータが配置されます。フロントは静的かつアクセス頻度が高いマスタ系データをレプリケーションキャッシュとして配置、バック側は主にトランザクション系データを分散キャッシュとして配置することで、分散かつ他のプロセスにバックアップを取ってくれます。コンフィグ次第で他サーバとかにバックアップを取るようにも出来るようです。またバックキャッシュではキャッシュストアといって、キャッシュに無ければDBに取りに行ったり、キャッシュに入ったものをDBへ入れたりする機能が動作します。
 Coherenceに関してコードを書くところは、キャッシュに格納するデータとなるエンティティモデル、上記のキャッシュストア、その他必要に応じてイベント処理やビジネスロジック側の為にDAO的なものを用意するくらいです。

 まず、エンティティモデルとしてPortableObjectを実装します。こんな感じです。

package jp.co.esoro.cache.EDM;

import java.io.IOException;

import com.tangosol.io.pof.PofReader;
import com.tangosol.io.pof.PofWriter;
import com.tangosol.io.pof.PortableObject;

/** 取引履歴 */
public class TrnRequest implements PortableObject{

	/** 取引年月日 */
	private String trnDate;
	/** 所属先ID */
	private String companyID;
	/** 取引番号 */
	private String trnID;
	/** 取消フラグ */
	private String canselFlg;
	/** ユーザーID */
	private String userID;
	/** 金額 */
	private long amount;
	/** 取引区分 */
	private String requestType;
	
	public String getTrnDate() {
		return trnDate;
	}
	public void setTrnDate(String trnDate) {
		this.trnDate = trnDate;
	}
	public String getCompanyID() {
		return companyID;
	}
	public void setCompanyID(String companyID) {
		this.companyID = companyID;
	}
	public String getTrnID() {
		return trnID;
	}
	public void setTrnID(String trnID) {
		this.trnID = trnID;
	}
	public String getCanselFlg() {
		return canselFlg;
	}
	public void setCanselFlg(String canselFlg) {
		this.canselFlg = canselFlg;
	}
	public String getUserID() {
		return userID;
	}
	public void setUserID(String userID) {
		this.userID = userID;
	}
	public long getAmount() {
		return amount;
	}
	public void setAmount(long amount) {
		this.amount = amount;
	}
	public String getRequestType() {
		return requestType;
	}
	public void setRequestType(String requestType) {
		this.requestType = requestType;
	}
	public String getId() {
		return trnDate + companyID + trnID;
	}

	@Override
	public void readExternal(PofReader arg0) throws IOException {
		setTrnDate(arg0.readString(1));
		setCompanyID(arg0.readString(2));
		setTrnID(arg0.readString(3));
		setCanselFlg(arg0.readString(4));
		setUserID(arg0.readString(5));
		setAmount(arg0.readLong(6));
		setRequestType(arg0.readString(7));
	}
	@Override
	public void writeExternal(PofWriter arg0) throws IOException {
		arg0.writeString(0, getId());
		arg0.writeString(1, getTrnDate());
		arg0.writeString(2, getCompanyID());
		arg0.writeString(3, getTrnID());
		arg0.writeString(4, getCanselFlg());
		arg0.writeString(5, getUserID());
		arg0.writeLong(6, getAmount());
		arg0.writeString(7, getRequestType());
	}
}

 上記の例ではキー項目が取引年月日と所属先IDと取引番号の3つですが、KeyValueなので1項目のキーとしてgetIdというメソッドを入れてます。
 
 次にバックキャッシュ側で動作するCacheStoreを実装します。こんな感じ。

package jp.co.esoro.cache.Cachestore;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;

import jp.co.esoro.cache.EDM.TrnRequest;

import oracle.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceFactory;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.tangosol.net.cache.CacheStore;

/**
 * 履歴テーブルキャッシュストア
 * */
public class ReqCacheStore implements CacheStore {
	protected static Logger logger;
	private Connection con = null;
	PreparedStatement storePs = null;
	PoolDataSource pds;
	
	private static String sql_TrnRequest = "MERGE INTO TrnRequest H "
			+ "USING (SELECT ? trnDate, ? companyID, ? trnID FROM DUAL) U "
			+ "ON (H.trnDate = U.trnDate "
			+ "AND H.companyID = U.companyID "
			+ "AND H.trnID = U.trnID) "
			+ "WHEN MATCHED THEN "
			+ "UPDATE SET canselFlg=? "
			+ "WHEN NOT MATCHED THEN "
			+ "INSERT (trnDate,companyID,trnID,canselFlg,userID,amount,requestType) "
			+ " VALUES (?,?,?,?,?,?,?)";
	
	public ReqCacheStore(String cacheName) {
		super();
		logger = LogManager.getLogger();
		try {
			pds = PoolDataSourceFactory.getPoolDataSource();
			pds.setConnectionPoolName(cacheName);
			pds.setConnectionFactoryClassName(   
			        "oracle.jdbc.pool.OracleDataSource");
			pds.setValidateConnectionOnBorrow(true);

			pds.setURL("****************");
			pds.setUser("user");
			pds.setPassword("password");
			pds.setInitialPoolSize(1);
			pds.setMinPoolSize(1);
			pds.setMaxPoolSize(20);
			
			con = pds.getConnection();
			storePs = con.prepareStatement(sql_TrnRequest);
		} catch (SQLException e) {
			logger.error(e.getMessage());
		}
	}

	@Override
	public Object load(Object arg0) {
		//get時キャッシュに無い場合DB等から読込みが必要な場合記述
		return null;
	}

	@SuppressWarnings("rawtypes")
	@Override
	public Map loadAll(Collection arg0) {
		//get時キャッシュに無い場合DB等から読込みが必要な場合記述
		return null;
	}
	
	@Override
	public void erase(Object arg0) {
		//削除が必要な場合記述
	}
	
	@SuppressWarnings("rawtypes")
	@Override
	public void eraseAll(Collection arg0) {
		//削除が必要な場合記述
	}
	
	/**
	 * DB書込み
	 * */
	@Override
	public void store(Object arg0, Object arg1) {
		try {
			TrnRequest trn = (TrnRequest)arg1;
			
			storePs.setString(1, trn.getTrnDate());
			storePs.setString(2, trn.getCompanyID());
			storePs.setString(3, trn.getTrnID());
			storePs.setString(4, trn.getCanselFlg());
			storePs.setString(5, trn.getTrnDate());
			storePs.setString(6, trn.getCompanyID());
			storePs.setString(7, trn.getTrnID());
			storePs.setString(8, trn.getCanselFlg());
			storePs.setString(8, trn.getUserID());
			storePs.setLong(10, trn.getAmount());
			storePs.setString(11, trn.getRequestType());
			
			storePs.executeUpdate();
			
		} catch (SQLException e) {
			logger.error(e.getMessage());
		}
	}

	@SuppressWarnings("rawtypes")
	@Override
	public void storeAll(Map arg0) {		
		for (Object entry :arg0.entrySet()){
			store( (Object)((Entry<?, ?>) entry).getKey(),(Object)((Entry<?, ?>) entry).getValue());
		}
	}
}

上記はDB書き込みのみの実装例です。

 この先はコンフィグを3つ書きます。凝った事をしなければフロント用もバック用も同じものでOKです。これらはJVM起動時オプションで指定します。クラスパス内ならファイル名だけ、外に置いてもフルパスで指定すればOKです。

 まず、キャッシュのクラスタ設定ですが、基本的にクラスタ名だけ書いておけば後は勝手に各プロセスが連携してくれます。
 起動オプションは、-Dtangosol.coherence.override=tangosol-coherence-override.xml

<?xml version="1.0" encoding="UTF-8"?>
<coherence xmlns="http://xmlns.oracle.com/coherence/coherence-operational-config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-operational-config http://xmlns.oracle.com/coherence/coherence-operational-config/1.2/coherence-operational-config.xsd">
    <!--coherence-version:12.1.3-->
    <cluster-config>
        <member-identity>
            <cluster-name system-property="tangosol.coherence.cluster">cohe-cluster1</cluster-name>
        </member-identity>
        <multicast-listener>
          <address system-property="tangosol.coherence.clusteraddress">224.0.0.1</address>  
          <port system-property="tangosol.coherence.clusterport">11131</port>
        </multicast-listener> 
    </cluster-config>
</coherence>

 次にPortableObjectとして実装したものをPOFコンフィグに書いておきます。番号は1000以上で適当に並べます。
 起動オプションは、-Dtangosol.pof.config=pof.xml

<?xml version="1.0"?>
<pof-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.oracle.com/coherence/coherence-pof-config"
    xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-pof-config http://xmlns.oracle.com/coherence/coherence-pof-config/1.2/coherence-pof-config.xsd">
	<user-type-list>
	<!-- include all "standard" Coherence POF user types -->
	<include>coherence-pof-config.xml</include>
	<user-type>
		<type-id>1001</type-id>
		<class-name>jp.co.esoro.cache.EDM.MstUser</class-name>
	</user-type>
	<user-type>
		<type-id>1002</type-id>
		<class-name>jp.co.esoro.cache.EDM.TrnRequest</class-name>
	</user-type>
	</user-type-list>
</pof-config>

 最後に各キャッシュ構成として、キャッシュのタイプや構成、作成したキャッシュストア等を書きます。
 起動オプションは、-Dtangosol.coherence.cacheconfig=cache-config.xml

<?xml version="1.0"?>
<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"
              xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config http://xmlns.oracle.com/coherence/coherence-cache-config/1.2/coherence-cache-config.xsd">
  <defaults>
    <serializer>pof</serializer>
    <socket-provider system-property="tangosol.coherence.socketprovider"/>
  </defaults>
  <caching-scheme-mapping>
    <cache-mapping>
      <cache-name>userMst</cache-name>
      <scheme-name>master-scheme</scheme-name>
    </cache-mapping>
    <cache-mapping>
      <cache-name>RequestTrn</cache-name>
      <scheme-name>trn-scheme</scheme-name>
    </cache-mapping>
  </caching-scheme-mapping>
  <caching-schemes>
    <replicated-scheme>
      <scheme-name>master-scheme</scheme-name>
      <service-name>master-service</service-name>
      <backing-map-scheme>
      	<local-scheme></local-scheme>
      </backing-map-scheme>
      <autostart>true</autostart>
    </replicated-scheme>
    
    <distributed-scheme>
      <scheme-name>trn-scheme</scheme-name>
      <service-name>trn-service</service-name>
      <thread-count>3</thread-count>
      <backing-map-scheme>
        <read-write-backing-map-scheme>
          <internal-cache-scheme>
      	    <local-scheme>
      	      <eviction-policy>LRU</eviction-policy>
      	      <high-units>1000000</high-units>
      	    </local-scheme>
          </internal-cache-scheme>
          <cachestore-scheme>
            <class-scheme>
              <class-name>jp.co.esoro.cache.Cachestore.ReqCacheStore</class-name>
                 <init-params>
                <init-param>
                  <param-type>java.lang.String</param-type>
                  <param-value>{cache-name}</param-value>
                </init-param>
              </init-params>
            </class-scheme>
          </cachestore-scheme>
          <write-delay>5s</write-delay>
          <write-requeue-threshold>1</write-requeue-threshold>
        </read-write-backing-map-scheme>
      </backing-map-scheme>
      <autostart>true</autostart>
    </distributed-scheme>
    
    <proxy-scheme>
      <scheme-name>proxy-scheme</scheme-name>
      <service-name>proxy-service</service-name>
      <thread-count>10</thread-count>
      <acceptor-config>
        <tcp-acceptor>
          <local-address>
            <address>192.168.111.113</address>
            <port>9099</port>
          </local-address>
        </tcp-acceptor>
      </acceptor-config>
      <proxy-config>
        <cache-service-proxy>
          <enabled>true</enabled>
        </cache-service-proxy>
        <invocation-service-proxy>
          <enabled>true</enabled>
        </invocation-service-proxy>
      </proxy-config>
      <autostart>true</autostart>
    </proxy-scheme>
  </caching-schemes>
</cache-config>

 上記ですとuserMstはレプリケーションキャッシュ、RequestTrnは分散キャッシュでOutOfMemory対策として1プロセス最大100万件まで、処理負荷分散の為にスレッドを3つ、キャッシュストアは応答レスポンスを意識してキャッシュ書き込み5秒後に非同期で動作するという内容です。最後のProxyはクラスタ構成プロセス外からのアクセス(*Extends)がある場合の受信口を用意している形になってます。なお、複数プロセスを動作させる場合は、PORTを個々に指定します。

カテゴリー: Java

作らないで済むなら作らない方がいい

 社内ネットワーク内で使う研修用の動画サイト作れないかな?と相談されたので、最初は作る前提でいろいろ調べていました。
 Javascript系のvideo.jsやMediaElement.jsとかで単純に動画を表示する所まではやってみましたが、動画のアップロードやメディア管理、認証等まで考えると結構な時間が掛かかりそうだと実感。。
 で、結局の所、WordPressを立ち上げてプラグインを入れる方がよいと伝えました。
 下手に作ると使う側にもこちらにもコストが掛かるし、使う側も何か問題があったら何も出来ずに問い合わせしてくるしかありません。WordPressなら情報量が豊富なので使う側も多少の勉強が必要とはいえググれば大抵の対処が可能だし、WordPressを理解していく過程で、別要件もこれで実現できそうだというのも結構ありそうです。
 作らなくて済むなら作らない方がいいというケースは、オープンソースのライブラリとかを使用するのも同じです。しかし、その見極めが結構難しくて、「定番」みたいなものなら別ですが、情報量が少ないものは使うと決めるまでに調べたり試してみたりの時間が掛かってしまうものです。

PayaraをCENTOS7で動かしてみる

 せっかくJavaEE7の仕事しているので、他にも何か作ろうかなと思い、自宅のVMにCENTOSを入れてPayaraを動かすことにしました。weblogicは高いし、glassfishは将来性が。。Payaraもglassfishなんですけど、将来性があるかと思い、こちらからMulti-Language Web Profileをダウンロード
http://www.payara.fish/all_downloads

なお自宅のVMはGIGABYTEのBRIX。
http://www.gigabyte.jp/products/product-page.aspx?pid=4581#ov
1年半くらい前にSSD250Gとメモリ8G*2を突っ込み合計8万くらいで作ったもので、VMware ESXiを入れてます。先日、Windows Server 2016 Technical Preview 4も仮想に入れてみました。入れただけですが。。これは今年9月くらいには使えなくなるみたいです。

 久しぶりにCENTOSをググってみるとバージョンが7になっています。4GあるDVDISOをダウンロードし、仮想マシンを作成してDVDISOからインストール。インストールウィザードだけで殆ど設定は完了。
centos7
次にTeraTeamからCENTOSに入ってJavaインストール

 yum search jdk java でちょうどいいのを探して、
 yum install java-1.8.0-openjdk.x86_64

次はダウンロードしたPayaraを、/optに解凍します。

で早速、Payaraを解凍したディレクトリpayara41/glassfish/binにあるstartservを実行。

動いたらしいので、Payaraの管理コンソールを表示してみます。 http://サーバIP:4848
あれれ、繋がらない?TeraTeamからサーバに入って、

ps -ef | grep java
 ちゃんとプロセスは動いています。

次はyum nmapでインストール後、ポートチェック。
 nmap localhost → 4848/tcp open appserv-http
 OKだけど、なんでだろう??

ネットワーク系問題の匂いがぷんぷんする中、原因をググっていくと、下記を発見
https://blog-kazuhisya.rhcloud.com/2014/06/15/getting-started-with-rhel7/

 firewall-cmd –permanent –add-port=4848/tcp
 やっとコンソールを表示できました。。が、リモートからだとデフォルトではコンソールにログイン出来ない模様。。

TeraTeamに戻って上記のbinで、
./asadmin enable-secure-admin
./asadmin change-admin-password
であとは再起動
./stopserv
./startserv &
やっと入れました。。
payara
で、何作ろうか?

VFS2でのFTP送信を追加

今作っているアプリケーションの要件に他サーバへ日次でFTP送信するというのがあり、シェルでいいかと思っていたのですが、既に別要件でVFS2を使っていたので、ついでにFTP送信もVFS2でやる事にしました。で、前に作ったクラスに下記を追加。

    /**
     * FTPファイルPut
     * @param 送信元DIR
     * @param 送信元ファイル
	 * @param サーバログインユーザー
	 * @param サーバログインパスワード
	 * @param サーバホスト名
	 * @param 送信先DIR
	 * @param 送信先ファイル名
     * @throws IOException 
     * 
     * */
    public void FTPputFile( String localpath, String File, String User, String Password, String HostName, String remotePath, String remoteFile) throws IOException {
    	String connURL = getConnectionURL(User, Password, HostName,"ftp");
		FileSystemOptions opts = new FileSystemOptions();

		SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(opts, "no");
		StandardFileSystemManager fsManager = new StandardFileSystemManager();
		fsManager.init();
    	try{
			FileObject localFileObject = fsManager.resolveFile(localpath + "/" + File,opts);
			
			if(!localFileObject.exists()){
				throw new IOException("File Not Exists");
			}
			FileObject remoteFileObject = fsManager.resolveFile(connURL + remotePath + "/" + remoteFile,opts);
			remoteFileObject.copyFrom(localFileObject, Selectors.SELECT_SELF);
    	}catch(IOException e){
    		throw new IOException(e);
    	}finally{
            fsManager.close();
            fsManager = null;
    	}
    }

	/**
	 * 接続URLを取得する
	 * @param サーバログインユーザー
	 * @param サーバログインパスワード
	 * @param サーバホスト名
	 * @param sftp ftp 等の文字列
	 * @return URL
	 * @throws UnsupportedEncodingException 
	 * */
	public String getConnectionURL(String User, String Password, String HostName, String FileSystem) throws UnsupportedEncodingException {
		return FileSystem + "://" + URLEncoder.encode(User, "UTF-8") + ":" + 
				URLEncoder.encode(Password, "UTF-8") + "@" + 
				URLEncoder.encode(HostName, "UTF-8");
	}
カテゴリー: Java

兵馬俑は事実を可視化したもの

 先日、上野の国立博物館に「始皇帝と大兵馬俑」を見に行きました。混んでる事を見込んで平日水曜日に行きましたが、平日でも結構混んでます。こういうのはじっくり見たいので、混んでると途端に気持ちがダウンしちゃうんですよね。。でも、展示の最後に職業柄考えさせられる一文、「永遠に皇帝であらんが為の事実を可視化する為に兵馬俑を作った」というニュアンスの事が書かれていたのに注視しました。
 システムに携わる人として可視化という言葉には、反応せざるを得ません。システムは画面以外目で見えないものなので、ドキュメントを書く事で可視化しますが、始皇帝は膨大なリソースを費やし、数千、万?のリアルな陶器を作る事で可視化したという事です。
 数年前に携わった、とある企業の基幹システムリプレイス要件定義フェーズで、一番受けが良かったと感じているのが、既存システム構成を可視化した「絵」です。中規模以上の企業ですと、複数のシステムが連携しながらその企業のビジネスの根底を担っていることが多いかと思います。でも、業務ユーザーの方々は自分の社内システムがどう構成されているかをそれほど把握していません。システム部門の方々は、どちらかというとインフラよりの観点が強いです。要件を聞き出す為に今の形が可視化された物が欲しいと思いましたが、既存資料に該当するものが無かったので、下記に気を付けて全体俯瞰図的な絵を書いてみました。
 1.1枚に収める(A4サイズにすべてを収めないとヒアリング時にすぐ見れない)
 2.サーバマシン等のインフラ単位ではなく、ユーザーが日常業務で言っているシステム名を1つの単位とする(利用部門等の境界が解る)
 3.1つのシステムが大きい場合はその中に分離できそうなものをサブシステムとして枠を引く(作業分担すべき境界が解る)
 4.重要データはどこにあるのか解るように入れておく(データ移行元とすべき場所が解る)
 5.システム間、外部、利用者との繋がりを矢印の線でつなぐ(in outが解る)
 6.完成を待たずに常に打ち合わせ時に持っていて見てもらい足りない所や間違いを指摘してもらう
こんな感じでした。
fukan

 その後、空いていた常設展示を見に行き、誰もが知ってる国宝になってる日本の埴輪を見ましたが、明らかにクオリティ的には兵馬俑の方が高いです。しかも、兵馬俑は埴輪に対し700年くらい古いものです。でも、緊張感に満ちた表情が殆どの兵馬俑よりも埴輪は「いい味」出してるなあという感じがします。これってただの自国びいきなんでしょうか?それとも強大な権力者から言われて作ったものよりも、自発的に作られたもの(真意は知りませんが)の方が共感を得やすいという事を表しているのでしょうか?

JSF2 Ajaxのエラーハンドリング

前回、画面エラー発生時のログ出力を整理しましたが、数秒毎に最新の状態にリフレッシュ表示する要件があり、Ajaxで実装してました。AjaxエラーのハンドリングはAjax側でハンドリングしなくてはなりません。
ネットで探してみたらOracleのサイトにjsf.ajax.addOnErrorというのが見つかりました。
https://docs.oracle.com/cd/E17802_01/j2ee/javaee/javaserverfaces/2.0/docs/js-api/symbols/jsf.ajax.html

JavaScriptのsetIntervalでフォームを5秒毎にリフレッシュさせているjsf.ajax.requestの箇所にエラーハンドリングを追加します。エラーが発生したらalertで問題発生をダイアログで表示させてますが、clearIntervalをしてもイベントが残っているみたいで、Alartのダイアログがしつこく出続けます。。仕方ないので1回だけ表示するように無理やり制御。

<script type="text/javascript">
var dispTimer1;
var errFlg;

errFlg = false;
var handleError = function handleError(data){
	clearInterval(dispTimer1);
	if ( errFlg == false){
		errFlg = true;
 		alert("情報の読み込みに失敗しました");
		location.reload();
	}
}

$(document).ready(function(){
  dispTimer1 = setInterval(function(){
    jsf.ajax.addOnError(handleError);
	jsf.ajax.request('mainform' , null,
            {execute: 'mainform',render:"mainform"}
    );
  }, 5000);
});
</script>

データ設計は誰でも出来るけど

先日、「Web Performer」というWebアプリ自動作成ツールを見る機会がありました。ま、AccessのWeb版といった感じです。こういう自動作成ツールは大抵、プログラムを書かずにアプリケーションが作れるというものですが、当然データ設計(ここではツールへの入力作業ですけど)をしなければ何も作れません。
 で、データ設計って誰でも出来るのかなという事を考えたのですが、基本的にそのシステムが対象とするドメインに関するナレッジが無いと設計出来ないので、どんなにスーパーなエンジニアでも、まったく知識が無いドメインを対象とするシステムを作る為のデータ設計は出来ません。作る前に業務分析とか要件定義のフェーズを設けて、それらの知識をエンジニアが得た上で設計し始めるといった手順になります。ま、そのフェーズにどのくらい時間をかける必要があるのかはまちまちですけど。。
 となるとデータ設計は確かにエンジニアがしなくてもいい範囲かも知れません。日ごろ使っているエクセルシートや紙の伝票などから大抵のデータ項目は決まってしまいますし。エンジニアがするのはそれを設計書という形に落とすだけです。
 でも、それらデータをつなぎ合わせるという感覚が一般の人ではちょっと難しいのかもしれません。データ設計をデータベース設計と言えるものにするには、最低限、データをテーブルとし複数のテーブルを定義して、それらの繋がりも明確にする必要があります。複数のデータを繋ぐにはエクセルの単一シートや紙には含まれていない可能性のある外部キー項目が必要になってきます。こうなると多少なりともRDBに関する理解が必要です。RDBなら大抵同じかも知れませんが、使用するデータベースミドルウェアの種類によっても、設計は違って当たり前です。物理設計となると完全にミドルの世界です。
 上記のような自動作成ツールを使う上では物理設計は殆ど不要みたいです。でも、データベース設計ができないとツールを使っても単一データ用の一覧・更新画面以上のものは出来上らないので、最低限RDBに関する理解は必要ですね。