Tera Termのログイン時のマクロ

サーバーにログインするたびにログ出力の設定を行うことが面倒なため、ログイン時にパスワードを入力するだけで日別にログ出力するマクロを作りました。
メモ代わりに置いておきます。

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
username = 'hogeuser'
hostname = 'hogehoge.net'
;keyfile = 'C:\id_rsa'
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

msg = 'Enter password for hogehoge.net '
passwordbox msg 'Get password' ;

msg = hostname
;; /auth=password....パスワード認証
;; /auth=publickey...公開鍵認証
strconcat msg ':22 /ssh /auth=password /user=' ; 
strconcat msg username
strconcat msg ' /passwd='
strconcat msg inputstr
;strconcat msg ' /keyfile='
;strconcat msg keyfile

connect msg


;; ログファイルパス等
LOGS_PATH = 'C:\Users\username\Documents\teraterm_log\'

;; ログファイルパス作成。
;;;; 同じサーバに複数のウインドウでログインする場合は
;;;; ファイル名に秒まであるほうが都合が良い。
;; getdate LOG_NAME 'hogesrv_%Y%m%d_%H%M%S.log'
getdate LOG_NAME 'hogesrv_%Y%m%d.log'

FULL_PATH = LOGS_PATH
strconcat FULL_PATH LOG_NAME

;; ログ取得開始(ファイルが存在すれば追記する)
logopen FULL_PATH 1 1

Ext JS 4でクラスシステム導入時に行ってみたいキャッシュバスターの設定

最近、自分が従事しているプロジェクトのウェブアプリケーションでは、Ext JS 4への移行に伴いクラスシステムを導入する流れが来ている。

ちゃんとExt JSの機能どおりにクラスシステムを導入すると、JavaScriptの実行中に動的にクラスファイル(jsファイル)をダウンロードすることになるのだが、そうなるとJavaScriptの動作が途中で止まってしまう。それを懸念する声がプロジェクトから挙がるのも無理はないことである。

現在、プロジェクトのシステムでは、jsファイルおよびcssファイルは、リリース直後では必ずサーバから取得させつつ、それ以降は次のリリースまで取得のリクエストさえさせない、ということを目指している。

リリース直後では当然既存のjsファイルが修正されているかも知れず、変にキャッシュが効いていると画面が正常に動かない。

それでいて、js/cssといった静的ファイルは一度取得してしまえば次のリリースまでは絶対に中身は変わらないため、それまではブラウザ側でキャッシュしてもらった方が断然良いわけである。

Ext JS 4のキャッシュバスターの設定

さて、Ext JS 4でサンプルを作り、jsファイルを動的に読み込む様子を見てみると、リクエストクエリの末尾に「?_dc=現在時刻」のようにブラウザにキャッシュさせないための記述(キャッシュバスター)が施されている。

GET /js/util/Test.js?_dc=1377937899901 HTTP/1.1
 →HTTP 200 OK

GET /js/util/Test.js?_dc=1377938345882 HTTP/1.1
 →HTTP 200 OK

GET /js/util/Test.js?_dc=1377939014299 HTTP/1.1
 →HTTP 200 OK

これではJavaScriptが動作して動的にjsファイルを読み込む記述があると毎回リクエストが行われ、かつ、毎回200 OKのレスポンスが返され、どうしても動作が遅くなってしまうように思われる。

ここで、このキャッシュバスターをさせないためには、次のようにExt.Loaderのコンフィグを指定する記述を行うことで実現できる。

Ext.Loader.config.disableCaching = false;

http://stackoverflow.com/questions/2047737/removing-dc-parameter-in-ext

こんな感じ。

GET /js/util/Test.js HTTP/1.1
 →HTTP 200 OK

GET /js/util/Test.js HTTP/1.1
 →HTTP 304 Not Modified
  (そもそもリクエストさえ行わない場合もある)

GET /js/util/Test.js HTTP/1.1
 →HTTP 304 Not Modified
  (そもそもリクエストさえ行わない場合もある)

しかし、これでは上述のようにリリース直後に画面が正常に動作しなくなってしまう。

そこで、キャッシュバスターとしてjs/cssファイルに付与するパラメータ「_dc」の値の現在時刻をリリース時に1つ決まる任意の文字列「リリース時刻など」に固定する。

ext-debug.js(Ext JS 4.2.1 版)では、6341行目と6353行目にそのような箇所が見える。

ext-debug.js : 6341行目

                (url + '?' + config.disableCachingParam + '=' + Ext.Date.now()) : url;

ext-debug.js : 6353行目

                noCacheUrl = url + (config.disableCaching ? ('?' + config.disableCachingParam + '=' + Ext.Date.now()) : ''),

本番リリース時はext-debug.jsではなくext.jsを使用すると思うので、その箇所の記述は上記のどちらも(もう少し絞り込んで)次のような記述に当たる。

"?"+B.disableCachingParam+"="+Ext.Date.now()

リリース直後にのみ効くキャッシュバスターの導入

ここで、この文中の「Ext.Date.now()」をリリース時刻などに置き換えてやれば良い。この処理は本番リリースを行う際に使用するであろうJenkinsなどのCIツール内で行えると良い。別途、この置換を行うスクリプトを用意しておいて、それをデプロイ手順に組み込むのである。

もしCIツールを使用していなかったりする場合は、リリース時の手順の中にそのスクリプトを実行する手順を組み込んでおけば良い。

もちろん「Ext.Date.now()」という文字列をそのまま検索して引っ掛けて「yyyyMMddHHmmss」のような形式のリリース時刻(ビルドorデプロイ時刻)にするとext.jsの他の箇所にも当たってしまうため、上記の記述をそのまま検索対象とし、末尾の「Ext.Date.now()」のみ書き換えることになる。

その他のjs/cssのキャッシュバスター

本記事には直接関係はないが、この「リリース時刻をjs/cssファイルの末尾に付ける」ということは、他のすべてのjs/cssファイルに対してリリース時に行っている。
デプロイ時にhtmlファイルなどを読み込み、js/cssを読み込む記述を見つけたらその末尾にデプロイ時刻を付与しているのである。
我ながらかなり強引な方法であるが、今のところうまく動作している。

他の方法では、jsファイルなどを読み込む記述(scriptタグ)をjspなどに書いて、キャッシュバスターに使用する文字列を日まで(yyyyMMddまで)にしておいたり、リリースバージョンIDのようなものを定義しておいてそれを使用する、という方法があるようである。
この場合は上記のようにリリース物件に文字列置換を行うようなことはしないで済む。

<script type="text/javascript" src="xxx.js<?= versionId ?>"></script>

みたいな感じでくっつけるのである。

しかし、日を使用している場合はリリースを朝一番に行い、誰もその日のyyyyMMddでファイルを取得していないことが条件となりそうである。そうでないとその日一杯はキャッシュ済みのリリース前のファイルを使用してしまいかねない。





Ext JS 4の書籍は次がオススメです。


Map.keySet()で取得したキーのセットをそのままローカル変数に入れるな! Map.keySet()は元のマップと関連付けられていることを意識せよ!

よくコーディングでjava.util.Mapを使用する。そのキーのセットを取得するにはMap.keySet()メソッドを使用することになる。
このメソッドで得られたSetは、元のMapのキーのビューになっている。つまり、Mapの内容に関連付けられているわけだ。

このSetを取得してからMapの内容を変更すると(Map.put(K, V)で追加、Map.remove(K)で削除、など)、そのセットの内容もそれに合わせて増減する。それを意識しないで使うと思わぬバグの原因になる。

// 例.商品IDと商品情報のマップ
Map<Integer, ItemBean> itemMap = new HashMap<Integer, ItemBean>();
// (1)ここでmapに値を設定
itemMap.put(itemId1, item1);
itemMap.put(itemId2, item2);
// ...

// (2)ここでマップに設定されたキー(例.商品ID)を取得する
Set<Integer> itemIdSet = itemMap.keySet();

// (3)ここでマップに値を追加、削除する
itemMap.put(itemId3, itemBean3);
itemMap.remove(itemId2);
// ....

// (4)ここで(2)の時点のsetの内容で処理を行おうとすると、内容は(3)によって書き換えられている
for (Integer itemId : itemIdSet) {
    // ....
}

また、これらはMap.values()で取得した値のコレクション、Map.entrySet()で取得したエントリーのセットに対しても同様である。

Map.keySet()で取得したセットをそのままローカル変数に代入しない

通常、Mapに関連付けられたセットではなく、Map.keySet()を実行したときのセットとして使用したい場合が多いと思われる。そのような場合はkeySet()で取得したセットをそのまま使用するのではなく、次のように取得と同時に別のセットかリストに変換すべきだ。

// セットを取得する場合
Set<Integer> itemIdSet = new HashSet<Integer>(itemMap.keySet());
// または
Set<Integer> originalItemIdSet = new HashSet<Integer>(itemMap.keySet());

// リストを取得する場合
List<Integer> itemIdList = new ArrayList<Integer>(itemMap.keySet());
// または
List<Integer> originalItemIdList = new ArrayList<Integer>(itemMap.keySet());

これだと元のマップを意識することなく、その時点での内容を保持したセットやリストとして使用できる。特にセットからセットを作る場合はオブジェクトを重複して作成しているようで無駄に感じる人もいるかもしれないが、ほんの少しのオーバーヘッドよりも誰にでもわかりやすい、誤解やバグの原因が少ないコーディングを心がけたいものだ。

Map.keySet()をマップに関連付けられたセットとしてそのまま使用する場合はローカル変数名を工夫する

また、あまり使用することもないと思われるが、マップに関連付けられたセットとして使用する場合には、ローカル変数名に何らかのマークを付けておくべきだ。
Map.keySet()で取得したセットだ、ということが分かれば意識できるのであれば、例えばitemIdKeySetという名前でもいいだろう。

FileクラスのgetXxxで取得できる文字列

Javaでファイルオブジェクトを使用してファイルを読み込んだりする際、そもそもFileオブジェクトの次のメソッドではどのような文字列が取得できるのか、試した結果を張っておきます。

使用したプログラム

package test;

import java.io.File;
import java.io.IOException;

public class TestFileString {

    /**
     * メインメソッド。
     *
     * @param args コマンドライン引数
     */
    public static void main(final String[] args) {

        System.out.println("main start");
        TestFileString main = new TestFileString();
        for (String s : args) {
            System.out.println(String.format("ファイル「%s」で実行", s));
            main.execute(s);
        }
        System.out.println("main end");
    }

    /**
     * 主処理。
     *
     * @param fileName ファイル
     */
    public final void execute(final String fileName) {

        File readFile = null;
        try {
            readFile = new File(fileName);

            System.out.println("readFile.getAbsolutePath()");
            System.out.println(readFile.getAbsolutePath() + "\n");
            System.out.println("readFile.getCanonicalPath()");
            System.out.println(readFile.getCanonicalPath() + "\n");
            System.out.println("readFile.getName()");
            System.out.println(readFile.getName() + "\n");
            System.out.println("readFile.getParent()");
            System.out.println(readFile.getParent() + "\n");
            System.out.println("readFile.getPath()");
            System.out.println(readFile.getPath() + "\n");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

実行結果1(ファイルの場合)

main start
ファイル「..\practice\pom.xml」で実行
readFile.getAbsolutePath()
C:\Documents and Settings\hoge\workspace\practice\..\practice\pom.xml

readFile.getCanonicalPath()
C:\Documents and Settings\hoge\workspace\practice\pom.xml

readFile.getName()
pom.xml

readFile.getParent()
..\practice

readFile.getPath()
..\practice\pom.xml

main end

getAbsolutePath()は、Fileコンストラクタに与えられたとおりのパスを踏まえてフルパスを返すのに対し、getCanonicalPath()では、重複した表現を回避し、本当のフルパスを返しているのが特徴だろうか。

また、getPath()では与えられたパスをそのまま返しているように見える。
getName()は、ファイル名だけが取得でき、getPath()では与えられたパスからファイル名と末尾のパスセパレータ(File.pathSeparator)を除いた文字列が取得される。

実行結果2(ディレクトリの場合)

main start
ファイル「..\practice」で実行
readFile.getAbsolutePath()
C:\Documents and Settings\hoge\workspace\practice\..\practice

readFile.getCanonicalPath()
C:\Documents and Settings\hoge\workspace\practice

readFile.getName()
practice

readFile.getParent()
..

readFile.getPath()
..\practice

main end

getAbsolutePath()、getCanonicalPath()、及びgetPath()についてはファイルと同じようである。

getName()は、ディレクトリ名だけが取得でき、getPath()では与えられたパスからディレクトリ名と末尾のパスセパレータ(File.pathSeparator)を除いた文字列が取得される。

VPNをつなぎながら社内のLAN環境やインターネットなどにアクセスする方法

VPNで本番環境などにつないでいるとき、社内の共有フォルダやTracサーバなどにアクセスできずに不便だ、という声があります。
この解消方法です。

VPNにつながっている状態で設定すると分かりやすいです(以下の手順はWindowsXP)。

  1. VPN で使用している仮想 LAN カードを特定する

    VPNを使用しているときに右下のタスクトレイで通信中になっているアイコンを見つける)
  2. アイコンを右クリックし、「状態(S)」をクリック
  3. 表示されたダイアログボックスの左下の「プロパティ(P)」をクリック
  4. 表示された LAN カードのダイアログボックスの中ほどで、「インターネット プロトコル (TCP/IP)」を選択して「プロパティ(R)」をクリック
  5. 「インターネット プロトコル (TCP/IP)のプロパティ」ダイアログボックスの「詳細設定(V)」をクリック
  6. 「IP 設定」タブの中ほどの「デフォルトゲートウェイ(E)」から、メトリックが「自動」となっているゲートウェイを選択して「編集(T)」をクリック
  7. 「自動メトリック」のチェックを外し、「5」(1より大きいがそれほど大きくない値)を入力して「OK」をクリック
  8. 今までのダイアログを全て閉じる

変更は以上で完了です。


このメトリックというのは、OSが通信で使用するLANカードの優先順位のようなもので、小さいほど優先順位が高いです。

通常、デフォルトゲートウェイVPNをつながない場合に使用している、仮想ではないLANカード(例:192.168.0.123)からつながるゲートウェイ(例:192.168.0.1など)が使用されます(このメトリック(優先順位)は1です)。

しかし、VPNをつないだ瞬間に、VPNの仮想LANカードからつながるデフォルトゲートウェイ(例:172.21.0.1)が候補として浮上します。
これら2つのゲートウェイに適切にルート探索の優先順位が付けられていれば問題はないのですが、後者のメトリックが自動である場合、優先順位が1となり、結果的に優先順位が既存のLANカードと衝突してしまいます。この場合、後から来たVPNデフォルトゲートウェイが勝ってしまうようで、そのデフォルトゲートウェイからしかルートを探さなくなってしまいます。

結果、192.168.0.1 からしか到達できないホストへのアクセスができなくなっています。

そこで、VPNの仮想LANカードのメトリックを1よりも大きくすることでルート探索を適切に行えるようにします。


これらの現在の優先順位などは、コマンドプロンプトから次を入力すると表示されます。

route print

今まで、いちいち社内環境にアクセスするごとにVPNを切断していた方は能率が上がるかと。

Linuxでの圧縮・解凍

圧縮

file1、file2、dir1、dir2 を comp.tar.gz ファイルに圧縮する。

tar -zcv file1 file2 dir1 dir2 > comp.tar.gz

zip comp.zip file1 file2 ...
zip -r comp.zip dir1

解凍

上記のように圧縮された comp.tar.gz ファイルを解凍する。

gzip -cd comp.tar.gz | tar xvf -

unzip comp.zip