全力で怠けたい

怠けるために全力を尽くしたいブログ。

ServerSocket の SO_REUSEADDR ソケットオプションの初期設定は誰が決めているのか

ふと ServerSocket の SO_REUSEADDR ソケットオプションが気になったときのメモ。

Java API 仕様

API 仕様には以下のように記述されている。ということは Java は我関せず?

ServerSocketが作成されるときに、SO_REUSEADDRの初期設定は定義されていません。アプリケーションはgetReuseAddress()を使用して、SO_REUSEADDRの初期設定を確認できます。

http://docs.oracle.com/javase/jp/8/api/java/net/ServerSocket.html#setReuseAddress-boolean-

SO_REUSEADDR ってなに?

SO_REUSEADDR は、サーバに TIME_WAIT 状態のアドレスに bind することを許します。

Programming UNIX Sockets in C - Frequently Asked Questions: サーバアプリケーションの作成 (TCP/SOCK_STREAM)
http://www.kt.rim.or.jp/~ksk/sock-faq/unix-socket-faq-ja-4.html

ちょっと乱暴だが、固定ポートに bind するサーバー以外では気にしなくても大丈夫ぽい*1

SO_REUSEADDR の初期設定

import java.io.IOException;
import java.net.ServerSocket;

public class PrintReuseAddress {

    public static void main(String[] args) throws IOException {
        boolean reuseAddress = new ServerSocket().getReuseAddress();
        System.out.printf("SO_REUSEADDR: %s", reuseAddress);
    }
}

これを手近なマシンで試してみた結果が↓これ。

まとめ

OS というか OS の TCP スタック実装によるのだろうか。
Java は我関せず?とも思っているけど、実は仕様で定められているのだろうか。

補足

OS X のデフォルトはやっぱり true の模様 http://lists.apple.com/archives/java-dev/2004/Dec/msg00570.html

*1:固定ポートに bind するクライアントもあるらしい

Invoke-WebRequestコマンドレットのエイリアスがcurl, wgetという落とし穴

職場 PC が Windows なのだが UNIX ライクなコマンドが使いたくて Gow を使っている。ところが、最近 PowerShell を 3.0 にアップデート*1したところ curl が上手く動かなくなった。調べたところ、Invoke-WebRequest コマンドレットのエイリアスcurl, wget が割り当てられているのが原因だった。忘れたときのためにメモしておく。
 

curl がエラーになる

PS >curl --silent http://www.yahoo.co.jp/
Invoke-WebRequest : 引数 'http://www.yahoo.co.jp/' を受け入れる位置指定パラメーターが見つかりません。
発生場所 C:\Users\ebi\bin\download.ps1:100 文字:1
+   curl --silent "http://www.yahoo.co.jp/"
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Invoke-WebRequest]、ParameterBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

 
エラーメッセージを読むと curl を Invoke-WebRequest コマンドレットとして実行しているのだが、まさかと思いつつエイリアスを調べてみるとそのまさかだった。
 

PS >Get-Alias -Definition Invoke-WebRequest

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Alias           curl -> Invoke-WebRequest
Alias           iwr -> Invoke-WebRequest
Alias           wget -> Invoke-WebRequest

 

解決

curl, wget のような紛らわしいエイリアスは要らないので削除する。

PS >Remove-Item -Path Alias:curl
PS >Remove-Item -Path Alias:wget

PS >Get-Alias -Definition Invoke-WebRequest

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Alias           iwr -> Invoke-WebRequest

 
これで curl.exe が使われるようになるがエイリアス削除は現在のセッションにしか反映されない。そこで $profile の中でエイリアス削除するようにした。
 

結論

Invoke-WebRequest コマンドレットに curl, wget みたいなエイリアスがあっても使いやすいとは思えない。と思ってしまうのだが、みんなどうなのだろう。

*1:ハンコが何個必要になったかは書かない

リスクに対処する方法

トム・デマルコ熊とワルツをを読んでいる。
9章「リスク管理のしくみ」ではプロジェクトを開始する前にリスク・リスト*1を作成し、それに対処する方法が書かれている。本文ではデンバー国際空港の自動手荷物処理システムの失敗を例えにしているのだが、読むうちに脳内で RPG 風の例えに変換されてしまった。しかし、なかなか分かりやすいように思えるしそう外れていないように感じたのでメモしておく。

リスクに対処する方法

熊とワルツをではリスクに対処する方法を4つ上げている。

  • 避ける
  • 抑制する
  • 軽減する
  • かわす
避ける

そのプロジェクトの中でリスクをともなう部分に手を付けないこと。リスクを避けると当然の結果として、リスクをともなう領域に入ることで得られる利益を見逃すことになる。

抑制する

リスクが実現した場合にかけなければならない時間と資金を準備しておくこと。個々のリスクを抑制してもあまり意味はなく、リスク全体をひとまとめに抑制するべき。

軽減する

リスクが実現する前に、後の抑制コストを軽減するための措置をとっておくこと。

かわす

上記のようなことは何もしなかったが、たまたまリスクの襲来をまぬがれる場合をいう。リスクをかわそうと計画する場合、天に祈るのが通例である。

ぼくがかんがえたさいきょうのたとえ

プロジェクトは、名作ドラゴンクエスト3*2のゲーム開始直後、アリアハンからナジミの塔にいって盗賊の鍵を入手することとする。道中には毒攻撃をしてくるバブルスライムや後列攻撃*3してくるフロッガーがいる。こいつらの出現確率は概して低いが、時として高い確率で出現することがある。

避ける

毒を受けたくないしうっかり後列の魔法使いを殺されても困るので勇者を廃業して自宅警備員になる。他の勇者候補が魔王を倒してくれればいいが、最悪の場合は魔王によって世界が滅びる*4

抑制する

毒を受けても大丈夫なように毒消し草を買ったり、フロッガーの後列攻撃をすぐ回復できるように薬草を多めに買っておく。しかし、毒を受けなかった場合は毒消し草の代金が無駄になるし、フロッガーが前列攻撃してきた場合はダメージが小さく済むため薬草の代金が無駄になる。

軽減する

フロッガーの後列攻撃に備えて後列の装備を充実させる。装備を充実させるためのお金がかかるが、後列攻撃を受けてもダメージが小さく済むので用意しておく薬草を減らせる。一度で攻略できず何度もアタックする場合は、抑制するよりも安くあがる。

かわす

毒消し草や薬草は用意しない。バブルスライムフロッガーに遭遇しないことを祈りながらナジミの塔に突入する。遭遇しても毒や後列攻撃を受けなければ万事OK!何かあったら即リセット。

結論

本文中の定義で十分だしそのほうが分かりやすかった。

*1:過去プロジェクトで発生した問題。当初の予想からどれくらい逸脱したか、その原因はなにかをリストにする

*2:初めてプレイした思い出深い RPG である

*3:主に標的になるのは守備力が低い魔法使いだろう

*4:ゲームシステム的にありえないが、そもそもなぜゲームしてるのかという話になる

Vrapper のインデント動作が変わった

仕事で使っている PC の Vrapper を久しぶりにアップデートしたらインデント動作が変わっていたので、試行錯誤した時のメモ *1
また、ここでのインデント動作というのは、ノーマルモードでの「<<」「>>」やインサートモードでタブキーを押した時の動作を指す。

環境

変わったこと

アップデート前は Eclipse の設定 *2 と同じように動いていた。
そのため、.vrapperrc にはインデント関係の設定は一切書いていなかった。

しかしアップデート後はインサートモードでは Eclipse の設定通りに動いてくれるが、ノーマルモードでの「<<」「>>」が明らかに Eclipse の設定とは違う。
たとえば、Tab policy を Tabs only にしているのに「>>」するとスペースが挿入されるといった動きになる。

リリースノートを読んでもそういったことは書いていない。 *3

したこと

Eclipse と同じ動作になるように .vrapperrc に設定しておく。
例えば、タブ文字でインデントするには以下のようにする。

set noexpandtab

スペース4個でインデント、既存のタブ文字をスペース12個分の幅で表示するなら以下のようにする。

set expandtab
set tabstop=12
set shiftwidth=4

*1:アップデート後は 0.48 だけど、その前のバージョンを控えておくのを忘れたので、どのバージョンで動作が変わったのかは分からない

*2:Preferences - Java - Code Style - Formatter で Edit ボタン押した後の画面の Indentation タブ - General settings で指定する Tab policy, Indentation size, Tab size

*3:ような気がする

Windows で UNIX の find みたいなことをする

背景

forfiles コマンドを使えば Windows でも UNIX の find コマンドみたいなことが出来る。
が、Windows XP だったりすると forfiles なにそれおいしいの?ということは珍しくないと思う*1

解決策

dir コマンドを使って劣化 find コマンドを作る。

@echo off
setlocal

set target=%1

set exec_cmd=%2
if "%exec_cmd%"=="" (
  set exec_cmd=dir /b
) else (
  set exec_cmd=%exec_cmd:"=%
)

shift
shift

for /f "usebackq" %%i in (`dir /b /s %target%`) do (
  %exec_cmd% %1 %2 %3 %4 %5 %6 %7 %8 %9 %%i
)

ここでは wfind.bat としてパスを通しておく。
使う時はこんな感じ。
第一パラメータに処理対象ファイル、第二パラメータに実行コマンドを指定する*2

C:\Users\eb>wfind *.txt
abc.txt
def.txt

C:\Users\eb>wfind *.txt type
C:\Users\eb\abc.txt
C:\Users\eb\def.txt

第二パラメータの実行コマンドになんちゃって sed みたいな VBScript を、そしてこのバッチファイル自体は他のバッチファイルから呼び出す、みたいにして使っていた。
昔を思い出したので書いてみたけど、XP のサポートが終了した今となっては全く価値のないバッチファイル。

本当に今は良い時代になったものだと思う。

*1:リソースキットから入れれば済む話だがそれが出来ない環境も珍しくなかった

*2:実行コマンドを指定しなかった場合には dir /b コマンドとして動くようにしてある

Mercurial をホームディレクトリにインストールする

背景

評価用に Mercurial を一般ユーザーのホームディレクトリにインストールしたので、備忘録までに。
状況としてはディストリビューションがものすごく古い、一般ユーザー権限しか持っていない、パッケージ管理コマンドが使えない…といった良くあるもので、条件は以下のような感じ。

  • ソースからインストールする
  • 一般ユーザーのホームディレクトリにインストールする
  • 今回インストールしたマシンは Ubuntu (古い)
  • HTML / man 形式のヘルプテキストは不要 (hg help が使えれば良い)

依存関係

SupportedPythonVersions - Mercurial *1
※HTML/man 形式のヘルプテキストが必要な場合は Docutils もインストールする必要がありますが、今回は hg help で賄えるのでインストールしません。

Mercurial
- Python
|- zlib |- bzip2

ソースアーカイブの取得

アーカイブ形式は tar.gz として *2 ~/src にダウンロード。バージョンは以下のとおり *3

インストール

*4

# zlib
$ cd ~/src
$ tar zxvf zlib-1.2.8.tar.gz
$ cd zlib-1.2.8
$ ./configure --prefix=$HOME
$ make
$ make install

# bzip2
$ cd ~/src
$ tar zxvf bzip2-1.0.6.tar.gz
$ cd bzip2-1.0.6
$ make -f Makefile-libbz2_so
$ make
$ make install PREFIX=$HOME

# make で -fPIC オプションを指定しろ〜と言われた場合には
# Makefile-libbz2_so の CFLAGS に追加する。
# (ex.) CFLAGS=-fPIC (省略) 

# Python
$ cd ~/src
$ tar zxvf Python-2.7.6.tgz
$ cd Python-2.7.6
$ ./configure --prefix=$HOME
$ make
$ make install

~/bin にパスを通すために ~/.bash_profile を編集。

# ~/.bash_profile
export PATH=~/bin:$PATH

続いて Mercurial をインストール。

# Mercurial
# HTML / man 形式のヘルプテキストは不要なので make install-home-bin
$ cd ~/src
$ tar zxvf Mercurial-3f83fc5cfe71.tar.gz
$ cd Mercurial-3f83fc5cfe71
$ make install-home-bin

確認

$ hg version
Distributed SCM (version 2.9.2)
(see http://mercurial.selenic.com for more information)

Copyright (C) 2005-2014 Matt Mackall and others
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

まとめ

依存関係の解決等、もっときちんとしたやり方があるはず。
パッケージ管理コマンドは偉大。

謝辞

不正確な記述、冗長な手順について指摘して下さった @ さん、ありがとうございました><

*1:ちなみに Python をインストール時に readline 等なども足りないと言われるが Mercurial を使う分には不要っぽいので入れていない

*2:展開コマンドを揃えたかっただけ

*3:Mercurial のバージョンがデバッグ版だとの指摘を頂いたので訂正しました

*4:ファイル名、ディレクトリ名は適当に読み替える

Windows 環境で hg convert で pserver 接続の CVS リポジトリに接続できない

状況

実際にしたこと

> cvs -d:pserver:user@repository.host:/repo/path checkout somemodule
> hg convert somemodule somemodule-hg 

hg convert を実行すると早々に「そんなリポジトリはない」と言われてしまいます。

なぜ?

convert extension は変換元リポジトリ (CVS) の変更履歴を取得しようとしますが、この時に接続情報として以下が必要です。

  • "BEGIN AUTH REQUEST"
  • CVSROOT
  • ユーザー名
  • パスワード (気休め程度に暗号化されたもの)
  • "END AUTH REQUEST"

これらを LF で結合して送信するのですが、実際に送信したデータを見ると CVSROOT の後ろになぜか CR がありました。
これは convert extension が CVSROOT を作業ディレクトリの管理ファイル (Root) から取得するときの処理が原因でした。

self.cvsroot = open(os.path.join(cvs, "Root")).read()[:-1]

WinCVS なので Root ファイルの改行コードも CRLF になっていて、その CR を取りこぼしてしまったようです *1
「これかー」みたいなことを Twitter で呟いていたら @ さんに「リストから取り出すときに strip() すればいいですよ」と教えて頂いたので、早速試してみたところちゃんと接続出来ました *2

他にも落とし穴が

変換元リポジトリには接続できたのですが、その後の変更履歴を取り出す段になって cvs log コマンドの解析に失敗しているのか、変換後のリポジトリでは全てのリビジョンでファイルが "新規追加" になってしまいました。
リビジョン数も明らかに少なく、@ さんに相談しながら色々と試したのですが、原因究明までは至らずギブアップとなりました。

その後

cygwincvs コマンドでチェックアウトしたものを hg convert したら一発で出来ました。
どう見ても不注意です。@ さん本当にすみませんでした。

*1:ここはものすごく端折っているけど、結局は不注意ですみませんということに。

*2:他にも [:-1] してるところがあるので注意が必要です