upicoで日本語ファイル名の画像がアップロード出来ない不具合を修正しました。

表題そのままです。ファイル名にマルチバイト文字が入っていた場合自動的に”_”に変換されていたのですが、何かの拍子に変換されなくなったのでファイルオープンの時に問題が起こっていました。現在は解消しています。それにしても、日本語ファイル名でアップロードする方が結構多いことにびっくりしました。
ちなみにファイル名が何の拍子に変換されなくなったのかがよくわからないです。Mojoliciousをアップデートした時か何かだと思うのですが、頑張って追いかけようと思うほど気になりもしないし、ファイル名そのままで保存してくれたほうがこちらとしてはありがたいので、まぁいいやと思いました。

Mojolicious::Liteでレスポンスヘッダを追加する。

Mojolicious::Liteでお好みのレスポンスヘッダをつけてrenderしたい場合の書き方。完全に忘れていたのでメモからひっぱりだしました。

get '/' => sub {
  my $self = shift;
  $self->res->code(200); #レスポンスのステータスコード
  $self->res->headers->header("Nyanco-Header" => "nyanconyan"); #Nyanco-Headerの値にnyanconyanを設定
  return $self->render(json =>{"a" => "b"});
}

ちなみにnginxでリバースプロキシしている場合、レスポンスヘッダのサイズが大きすぎると「upstream sent too big header while reading response header from upstream」を返してきます。大きなレスポンスを返す予定の場合、proxy_buffer_sizeあたりの設定を見なおしたほうがいいかもしれませんね。

以下Mojoliciousのドキュメント。
Mojo::Message::Response – HTTP response

ConversationScopedはスコープを抜けてもメンバ変数は初期化されない。

問題

下記のようなコードがあったとして、BooBooクラスのスコープを抜けたら(startConversationを呼んだ後、endConversationを呼んでリダイレクトすると、)リダイレクト先のbooBoo.memはどうなるかという問題。

	// Bean側
	private String mem = "My name is Default_Boo.";
	public String startConversation(){
		//会話が開始していない
		if(conversation.isTransient()) {
			conversation.begin();
		}
		mem = "My name is Changed_Boo.";
		return REDIRECT_URL;
	}
	
	public String endConversation(){
		//会話が開始している
		if(!conversation.isTransient()) {
			conversation.end();
		}
		conversation.begin();
		//名前はデフォルトに戻っているはず…
		return REDIRECT_URL;
	}
<!-- リダイレクト先 -->
<h:outputText value="#{booBoo.mem}" />

正解

当然ですが名前はデフォルトに戻らないです。ちゃんと初期化しましょうという話でした。ていうかそもそもこんなコード書いちゃダメじゃねって気がしますが、どうなんでしょう。書いたのが自分じゃないので何とも言えない。

この記事書いてから下記の記事を見つけました。会話終了後に一旦リダイレクトするのはたしかにスマート、というか本来こうすべきなのでしょうね。
JSFで会話スコープを終わらせつつ自画面に遷移 – じゃばらの手記

ConversationScopedは複数クラスごとに管理できない。

タイトルだけだと多分何言ってるかわかんないと思います。私も何言ってんのかよくわかんなかったです。
前提はJava EE 7(GlassFish 4.1)で使用しているJSFは2.2です。

事情

ClassAとClassBはともにConversationScopedで、ともにconversationをInjectしている。
とあるひと「ClassAとClassBはConversation別々に管理できるよね!」
つまり、下記のような感じです。

  • ClassAでConversation(以降長いので会話と表現)を開始してもClassBでは会話は始まっていない。
    ClassBで明示的に会話を開始する必要がある。
  • 逆に、ClassAで会話を終了してもClassBで始まっている会話は終了しない。

ぼく(そのかんがえはおかしいのでは…)

説得フェーズ

とあるひとに事情を説明するのにコードを書きました。
具体的な動作としては

  • ConversationScopedの牛(MowMowクラス)と猫(NyanNyanクラス)が存在。
  • JSFでそれぞれの会話開始ボタンをクリックすると、牛と猫の名前が変わる。
  • JSFでそれぞれの会話終了ボタンをクリックすると、さて、牛と猫の名前はどうなるでしょう。

とあるひとの主張が正しければ、牛の会話ボタンを先に押しても、後で猫の会話開始ボタンを押したときに猫クラスのconversation.isTransientはtrueのはず。
かつ、牛と猫で会話を始めて、先に牛の会話を終了させても猫の名前は変わったままのはず。

結果

牛の会話ボタンを押しても、後で猫の会話開始ボタンを押したときに
猫クラスのconversation.isTransientはfalse、かつ牛との会話を終わらせると猫の名前も一緒に元に戻りました。

実際に書いたコード

長くて貼れないのでGitHubに上げました。
retrorocket/conversation-example
SessionScopedも一緒に検証したので実際にはSessionScopedな犬(WanWanクラス)がいます。

捕捉

とあるひとの主張を実現できるような方法があるのか調べたのですが見つからなかったので、ないんじゃないかなぁと思いました。(小並感