勘違いから変な事を思い悩んだのでメモ…。
マルチスレッド化可能なクラスを作ってて、内部で使ってる同期の仕組をクライアントに公開し…クライアントと同期の連携を取る。…という事を考えた。で、それにはLockを公開すればいいのか、synchronized用のモニタを公開すればいいのか?…どっちなん?と。
で、もうたぶん、スタートがおかしいんだけど…泣…そこから…以下の様に発想…。
- Collections.synchronizedCollection(c)はthisのモニタで連携を取れと書いてる…?
- でも、そんなのはクライアント側の好みや都合があるから、好きな方を選べるようにすべき。
- synchronizedかLockか選べるようにするには…意外難しい…?
- ロックの取得と解放から本体処理への流れをメソッド化…??
- で、それを…
- オーバライド出来るようにする…???
- コンストラクタで指定したフラグでロックの方針を切り替える…???
- どっちも激しく変!
結局、まず間違いは、内部で使う不変条件を保つための同期と外部で使う複数のメソッド呼び出しを繋ぐための同期を連携という名の元に混ぜてしまった事。こいつらは確かに混ぜる事は出来るけど、出来るからといってすべきじゃない。分けた方が各同期用オブジェクトの意味がはるかに分かり易くなる。分けるとだいたいこんな感じになる。
- 内部
- その場に適した同期方式を使う。Lockにしろモニタにしろ、protected又はprivateにし、外に公開しない。
- 外部
- 同期ラッパーと手動の同期方式を組み合わせる。大事なのはその両方で同じ同期方式を使う事だけ。Collections.synchronizedCollection(c)ではモニタを使う方針だけど、気に入らなければLockを使うラッパーとそれを使った手動の同期でも構わない。
使用に当たっては、外部の対策だけでもオールラウンダーになれる。逆に内部の対策だけでは無理なのでラップが必要。ただ、内部のみの場合、ロック時間が短いのでその分優位。で、結局、大事なのは、内部でも外部でもLockだろうがsynchronized用のモニタだろうが、お好みの同期方式を使えたって事…。
うーん、微妙な解釈の違いが蓄積して最後は意味不明に…か。例えるなら、仕様書の森での遭難…かな。…ん?…全然うまく言えてない?……もう寝よう…はぁぁ…。
一つ前の記事、クリックの自動化だけど…間違い発見! というのもHTTPヘッダのRefererが送信されてない! 確かにIEからXHRを立ち上げたらばこのRefererの偽装は問題になるけど…WSHから作ってるわけだし、、、もぅ〜このMSの頑固じじぃ!!…と取り敢えず人のせいにしておく…。
で、解決策なんだけど…IEのOLEオートメーションで処理する。これで前の方法と同じく、Windowsなら結構古くても何もインストールや設定をしなくても、取り敢えず動く。ただ、この方法…ちょっとHTTPでGETしたいだけなのに…IEを立ち上げるはめになる。…処理がやや重く、エンジニアとしては気持ち的にも重い…。
<?xml version="1.0" encoidng="utf-8"?>
<job>
<script language="JScript">
<![CDATA[
/* ブログランキング、クリック用自動化スクリプト */
// 各種プロパティ
var URL = "http://nossie.blog71.fc2.com/";
var CLICK_URL = "http://blog.with2.net/link.php?608044";
var MOVETO_URL = "http://blog.with2.net/rank";
var ECHO_TROUBLE = true;
// 自動化部分
var ie = null;
try {
// IEの起動
ie = WScript.createObject("InternetExplorer.application","ie_");
ie.visible = false;
ie.navigate(CLICK_URL, null, null, null, "Referer: " + URL);
/* WSH機能解説 *
* スリープしている間に名前で暗黙的にバインドされた
* イベントが呼び出されます。スリープしなかった場合、
* 勝手に終了してしまうようです。*/
WScript.sleep(10000);
// 指定時間内に終わらなければエラー
throw new Error(0, "Time Out!!");
} catch(e) {
atError(e);
}
function ie_NavigateComplete2(pDisp, url) {
if(url.indexOf(MOVETO_URL) == 0) {
pDisp.quit()
WScript.quit();
}
}
function atError(e) {
if(ie != null)
ie.quit();
if(ECHO_TROUBLE)
WScript.echo("blog click fail!!:" + e.description);
WScript.quit(1);
}
]]>
</script>
</job>
パソコンを起動した時とか、日付の変わった直後にどっかのサイトにアクセスしてデータをどーのこーの…というテーマは結構ある。…例えば、どっかの株価欄をチェックして警告を出したり、ちょっとダサいけど自分のブログランキングのボタンを自分でぽちっとしたい時、(IPアドレスでカウントされるので一票だけならこうした自作自演も出来ちゃう…)。
で…まぁ、Firefox等の高機能なブラウザとちょっとした自動化スクリプトがあれば朝飯前だけど、そもそもブラウザ立ち上げるのも面倒…という怠け者に何かいい方法はないのか…という話。
最初に考えたのはバッチとtelnetの組み合わせ。だけど、Vistaではtelnetが初期状態で有効になってない。しかも、telnetは入力から応答までのタイムラグやら何やらに気を配る必要があるから、単純にパイプでサーバへの接続からGETメソッドの発行を入力しても、sleep等しないとうまくいかない。そこで、WSHを使ってみたらす〜んなり簡単プログラムである事が判明。そっかWebプログラミングと同じでXHRを使えば良かったんだ…って何故こんな事に気づかなかったんだろう…。
(PowerShellでなく古いWSHを使う理由は外出先で常に.Netが使えるとも限らないから)
以下のコードは当ブログの人気ランキングボタンのクリックをエミュレートしたサンプルコード…。
ちなみに、相手サーバとのやり取りの過程で、途中 Refresh HTTPヘッダによる折り返しがあるんだけど…これがランキングに影響するかは…謎。
<?xml version="1.0" encoidng="utf-8"?>
<job>
<script language="JScript">
<![CDATA[
/* ブログランキング、クリック用自動化スクリプト */
// 各種プロパティ
var SERVER_URL = "http://blog.with2.net"
var URL = SERVER_URL + "/link.php?608044";
var REFERER = "http://nossie.blog71.fc2.com/";
var REFRESH_CONTENT_REG = /<META\s+HTTP-EQUIV="Refresh"\s+CONTENT="([^"]*)"/i;
var REFRESH_DETAIL_REG = /\d+;URL=(.*)/
// 自動化部分
try {
// クリックによるリクエストの再現
var xhr = WScript.createObject("Msxml2.XMLHTTP");
xhr.open("GET",URL,false);
xhr.setRequestHeader("Referer",REFERER);
xhr.send();
if(xhr.status != 200)
throw new Error;
// 結果の取得
var result;
var rText = xhr.responseText;
result = REFRESH_CONTENT_REG.exec(rText);
if(result == null)
throw new Error;
var refreshContent = result[1];
result = REFRESH_DETAIL_REG.exec(refreshContent);
if(result == null)
throw new Error;
var refreshLocale = result[1];
// リフレッシュ要求先へ転送
var refreshUrl = SERVER_URL + refreshLocale;
xhr.open("GET",refreshUrl,false);
xhr.setRequestHeader("Referer",REFERER);
xhr.send();
if(xhr.status != 200)
throw new Error;
WScript.Quit();
} catch(e) {
// エラーがある場合はエラーコードを出力し終了
WScript.Echo("blog click fail!!:" + e.description);
WScript.Quit(1);
}
]]>
</script>
</job>


