GlassFish4.0でJAX-RSのリソースにCDIでインジェクト出来ない問題

コードは次のとおり

import文等は省略してます。

TestBean.java
@ApplicationScoped
@Getter // lombok
public class TestBean implements Serializable {
    private Integer count = 0;

    public void increment() {
        count++;
    }
}
TestResource.java
@Path("test")
@RequestScoped
public class TestResource {
    @Inject private TestBean testBean;

    @GET
    @Produces(MediaType.TEXT_HTML)
    public String test() {
        testBean.increment();
        return testBean.getCount().toString();
    }

    @GET
    @Path("hoge")
    @Produces(MediaType.TEXT_HTML)
    public String hoge(@NotNull @QueryParam("id") int id) {
        return String.valueOf(id);
    }
}

これ、なんで2つ目のメソッドを書いたかは後でわかります。

ここで"/設定されたパス/test"にアクセスすると次のようなエラーになります

org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee以下略
     at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:74)
     at org.jvnet.hk2.internal.Utilities.justInject(Utilities.java:771)

対処法

GlassFish4.0でJAX-RSのクラスにCDIでインジェクションしようとしたらUnsatisfiedDependencyExceptionが発生するバグ?

そもそも、もし4.0.1の正式版が出た後で、それを使っているならこの問題は出ないはずですが、まだ正式版が出ていないので一時回避的に対応します。

対処法1

上のリンク先に対処法が書いてあります。

バグのチケットはこれ
https://java.net/jira/browse/GLASSFISH-20597

ここで書かれている対処法はglassfish4のフォルダ内のjarファイルを置き換える
jersey-gf-cdi.jar -> jersey-gf-cdi-2.0.jar
というものですが、なんかうまくいかなかったので次のようにしました。

対処法2

まずは次のリンク先でglassfishのzipファイルを取得します。
http://dlc.sun.com.edgesuite.net/glassfish/4.0.1/nightly/
※ これは正式版ではないのでその点は注意してください
こちらで動作確認したのは「07-Nov-2013 06:55」に作成されたzipファイルです(約100M)
これでもって動かせば少なくとも今回エラーになったところは動くようになりました。

ただし、4.0.1になると例えばJerseyのバージョンが2.4になったりするので、それによる不整合が起こる可能性もあります。

補足

この現象は毎回出るわけではないようです。
試しに上のTestResource.javaの2つ目のメソッドを削除してGF4.0で試してみるとうまくいきます。

で、どこが原因なのかなといろいろ試してみたところ@NotNullアノテーションを外せばうまくいきました。

と、ここまできて再度上のJIRAのページ(再掲)を見てみるとちゃんと書いてありました。

It is problem that a JAX-RS application (exactly a resource class) throws org.glassfish.hk2.api.UnsatisfiedDependencyException when I send GET request to it. The application uses both Bean Validation (@NotNull and @Min) and CDI (@Inject) into same resource class and it is neither Managed Bean nor EJB (Session Bean). If it uses either of them (for example, it uses without Bean Validation), it runs well and send response as status 200.

下記の記事にもあるようにGF4.0はまだ不完全なままリリースされたようです。
http://yoshio3.com/2013/06/11/glassfish-v4-0-released-0611/
http://www.publickey1.jp/blog/13/java_ee_7glassfish_4.html
そのあたりは理解した上で今のところは「試す」というスタンスのほうが良さそうです。

Jersey MVCでResponse Headerにcharsetを指定する

JAX-RS実装であるJerseyですが、通常の画面をMVCで構築するための拡張が用意されています。
https://jersey.java.net/documentation/latest/mvc.html

基本的な使い方は上のリンク先を見てもらうなりぐぐってもらうなりして、今回Responseヘッダにcharsetを指定しようとしたときにちょっとつまずいたポイントがあるのでメモしておきます。

まずはシンプルな例です。

    @GET
    @Path("list.html")
    @Produces(MediaType.TEXT_HTML)
    public Viewable getList() {
        Map<String, Object> it = new HashMap<>();
	~~中略~~
        return new Viewable("list", it);// "list"でもいい
    }

ただし、これだとcharsetが指定されていないためクライアント側で文字化けが起こることがあります。

シンプルな方法

ひとつめの方法としては、@Producesにcharsetまで指定する方法があります。

    @GET
    @Path("list.html")
    @Produces(MediaType.TEXT_HTML + "; charset=UTF-8")

これは毎回書いておく必要があるのでちょっと面倒かもしれません。

もうひとつの方法としてはhtml内で指定します。

    <meta charset="UTF-8">

これは比較的部品化しやすいのでそれほど面倒ではないと思います。

ここで上げた2つの方法は片方だけでも問題ないと思いますが、両方しておくデメリットもないので両方しておきましょう。

filterを使う

上の@Producesにcharsetまで指定する方法がちょっと面倒なので全てのレスポンスにcharsetを自動でつけれないかと考えるのは自然なことだと思います。
これを可能にするのがfilterです。
https://jersey.java.net/documentation/latest/filters-and-interceptors.html

下記のようなクラスをつくりResourceConfigに登録すればOKです。

@Provider
public class CharsetResponseFilter implements ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        MediaType mediaType = responseContext.getMediaType();
        if (mediaType != null) {
            if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
                responseContext.getHeaders().putSingle("Content-Type", new MediaType(mediaType.getType(), mediaType.getSubtype(), "UTF-8"));
            }
        }
    }
}

これで、Content-Typeがtext/htmlのレスポンスのcharsetはUTF-8となります。
ただし、
Jersey MVCを使っているとうまくいきません。
なぜかというと設定が上書きされるからです。
※ GlassFish4.0で見ているのでjerseyのバージョンは2.0です。
他のバージョンでは確認できていませんのであしからず。

詳しく見ていきます。
まず、処理の順序としては次のとおりです。

  • 11 リソースメソッド実行
  • 12 ContainerResponseFilters are executed(上のfilterです)
  • 14 MessageBodyWriter is executed

https://jersey.java.net/documentation/latest/filters-and-interceptors.html#d0e6796
で、jersey MVCでは上のMessageBodyWriterの実装クラスは
org.glassfish.jersey.server.mvc.internal.ViewableMessageBodyWriter
です。
この中で次のように実装されています。

    @Override
    public void writeTo(final Viewable viewable,
                        final Class<?> type,
                        final Type genericType,
                        final Annotation[] annotations,
                        final MediaType mediaType,
                        final MultivaluedMap<String, Object> httpHeaders,
                        final OutputStream entityStream) throws IOException, WebApplicationException {
        final Template template = TemplateHelper.getTemplateAnnotation(annotations);

        try {
            final ResolvedViewable resolvedViewable = resolve(viewable, template);
            if (resolvedViewable == null) {
                throw new WebApplicationException(
                        new ProcessingException(LocalizationMessages.TEMPLATE_NAME_COULD_NOT_BE_RESOLVED(viewable.getTemplateName())),
                        Response.Status.NOT_FOUND);
            }

            httpHeaders.putSingle(HttpHeaders.CONTENT_TYPE, resolvedViewable.getMediaType());
            resolvedViewable.writeTo(entityStream);
        } catch (ViewableContextException vce) {
            throw new NotFoundException(vce);
        }
    }

この中で、Content-Typeがセットされなおしています。
パラメータでMediaTypeが渡されていますが、これがfilterでセットされている値です。しかし、このメソッド内ではそれを使わずにresolvedViewable.getMediaType()をヘッダにセットしています。これは@Producesに指定した値です。

というわけでfilterとかを使って共通処理としてcharsetをヘッダに指定するのはちょっと難しそうです。

struts2における静的ファイルについてのメモ

静的ファイルへのアクセス

通常jsやcssのファイルはWebContentやsrc/main/webapp(Maven Projectの場合)の中に置いて直接参照させます。
例えば、WebContent/css/main.csshttp://address/コンテキスト名/css/main.cssでアクセスできます。
もうひとつの方法として、社内フレームワークのようなものをstrutsのプラグインとして作る場合にプラグイン内に埋め込むということが出来ます。
簡単に言うと、下記のパッケージ(フォルダ)にあるファイルを/コンテキスト名/struts/ファイルへのpath/ファイル名でアクセスできます。もしくは/struts/の代わりに/static/でも同じ。

  • org.apache.struts2.static
  • template
  • org.apache.struts2.interceptor.debugging
  • static

例えば、struts2-jquery-pluginにある
template.js.base.jquery-1.x.x.jsは
/コンテキスト名/struts/js/base/jquery-1.x.x.jsでアクセス可能です。

ちなみに、デフォルトの状態だと
/static/simple/a.ftl
のようなアクセスが有効になってしまいます。
これはセキュリティ的にどうなのかなとちょっと思ってしまいます。

基本的な動作の流れ

デフォルトではorg.apache.struts2.dispatcher.DefaultStaticContentLoaderによってハンドリングされます。設定で変更可能です(後述)
クライアントからアクセスがあると、まずFilter#doFilter()が呼ばれます。
Filterはorg.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilterという前提で以下は説明します。FilterDispatcherはDeprecatedなので今回は無視します。
FilterはActionMapper#getMappingを呼び、対応するアクションクラスを探します。
見つからない場合、StaticContentLoaderの出番です。/static/や/struts/で始まっている場合、上記のフォルダからマッチするファイルを探してそれをクライアントに返します。
StaticContentLoaderも対象ファイルを見つけられない場合は、次のFilterに処理が渡されます。
通常は他のFilterはないと思うのでこの時点でWebContentやsrc/main/webappを見に行きそこにあるファイルがクライアントに返されます。

静的ファイルに関する設定

WebContentやsrc/main/webapp内の静的ファイルへのアクセスのパフォーマンス向上

上記のように、WebContentやsrc/main/webappにおいてあるファイルにアクセスする際は少しオーバーヘッドがあります。この処理をスキップさせる設定が可能です。
struts.xmlに下記のように追加します

<constant name="struts.action.excludePattern" value="/js/.*, /css/.*, /img/.*" />

これらのパターンにマッチするリクエストは、ActionMapper#getMappingやStaticContentLoaderの処理がスキップされます。

static contentとして探すpathを追加

上記の4つのpath以外に追加したい場合の設定です。
例えば、hoge.staticパッケージを追加したい場合はweb.xmlのフィルターの設定を書き換えます。

<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
<init-param>
<param-name>packages</param-name>
<param-value>hoge.static</param-value>
</init-param>
</filter>

これは起動時にDefaultStaticContentLoader#setHostConfig(HostConfig)で参照され、上記の4つのpathと同様に扱われます。

/struts/~~による静的ファイルへのアクセスを停止する

struts.xmlに下記の設定をするだけです。

<constant name="struts.serve.static" value="false" />

ただし、このようにすると独自でそれらのファイルを展開する必要があります。
パフォーマンス向上のための手段として使えます。
この値はDefaultStaticContentLoader#canHandle(String)で参照され、falseの場合は静的ファイルの探索は行いません。

静的ファイルをハンドリングするクラスをカスタマイズする

StaticContentLoaderをimplementsして新たなクラスを作成した上でstruts.xmlに設定を追加します。
この設定をしない場合、DefaultStaticContentLoaderが使用されます。

<bean type="org.apache.struts2.dispatcher.StaticContentLoader" class="MyStaticContentLoader" name="myLoader" />
<constant name="struts.staticContentLoader" value="myLoader" />

参考ページ
http://struts.apache.org/release/2.3.x/docs/static-content.html

freemarkerの設定

設定は3つのレイヤで可能

上位からEnvironment,Template,Configurationです。それぞれ、Configurableクラスを継承しています。
http://freemarker.sourceforge.net/docs/pgui_config_settings.html
より上位のレイヤの設定内容が有効となります。例えばEnvironmentとConfigurationで同じパラメータに対し異なる値が設定されていてもEnvironmentの設定が有効となります。

  • Configurationは主に設定ファイルによって値がセットされます。もちろん直接設定してもOKです。
  • Templateに設定をすることは基本的にやらない
  • Environmentにおける設定はテンプレートファイル内において下記のように行う。
<#setting locale="it_IT">
<#setting number_format="0.####"> 

設定項目

3つのレイヤに共通の設定とConfigurationだけの設定があります。
http://freemarker.org/docs/api/constant-values.html
freemarker.core.Configurableが共通、freemarker.template.Configurationが専用。

いかにいくつか例を挙げます

date_format

日付のフォーマットを指定。
例えば

date_format = YYYY/MM/dd

datetime_formatも同様

number_format

数値を表示する際のデフォルトのフォーマットを決める。
例えば

number_format = #

としていると${123456.78}は「123457」と表示され

number_format=000,000,000.000

とすると「000,123,456.780」と表示され

number_format=,###.###

とすると「123,456.78」と表示される。

これらのdate_formatやnumber_formatはデフォルトの振る舞いであって、${dateValue?string("MM/dd")}や${numValue?string(",###")}みたいにすれば個別に指定可能です。

whitespace_stripping

trueに設定すると余分な空白を取り除いてくれます。
全ての空白というわけではないです。詳細未確認。

auto_include

それぞれのテンプレートファイルで宣言しなくても毎回includeしてくれます。

auto_include=/path/to/include.ftl, /path/to/include2.ftl

anto_importも同様です。

auto_import=/path/to/import.ftl as i, /path/to/import2.ftl as i2
default_encoding, output_encoding, url_escaping_charset

文字コードの設定です。
default_encodingはテンプレート読み込みの際に使用するエンコードを設定します。
url_escaping_charsetは画面にurlを${foo?url}のように埋め込む際にエスケープするエンコードを設定します。nullの場合はoutput_encodingが使用されます。

CodeIQ 目指せお釣りマスター!を解きました

オリジナルの問題
http://pavlocat.hatenadiary.jp/entry/2013/08/25/181515

今回は商品10個、商品を渡す順番とお金の渡し方を考えます。

@ozy4dmさんによるコード
https://bitbucket.org/ozy4dm/otsuri_master


私の解答です(Java)。
https://gist.github.com/kamegu/7149594

実行時間は1300msくらいでした。
10->20件にすると約5分かかります。


解説記事を見てみると、まだまだ工夫の余地がありそうです。
以下はいただいたコメントです。

最適解を求めるだけでなく、アルゴリズムに工夫もあって素晴らしいです!
大規模な問題になると、探索順序はプログラムの性能に大きく影響してくると思います。その辺は私自身もまだ研究中なのでよくわかりませんが、もうちょっと考えてみるつもりです。
もし良いアイデアが思いついたら教えてください。

とはいえ、@ozy4dmさんさんはRubyで0.1秒とのことなのでなかなか悔しいです。


と思ったら前提が崩れてました。
https://twitter.com/wonderful_panda/status/393558031671111680
https://twitter.com/wonderful_panda/status/393562017140703232



100商品でといた人はいるんでしょうか?

struts2 / Freemarkerでpublicフィールドを参照できるようにする

Freemarkerでは通常getterを見るようになっていてpublicフィールドは参照しません。
ちなみにこれは

${hoge.hoge1}

みたいな場合で

<@s.property value="hoge.hoge1"/>

のようにすればpublicフィールドも参照可能です。(これはfreemarkerではなくstruts2のValueStackを使っているため)
なので、フィールドはprivateにしてgetter/setterを使うというのが正攻法だと思います。
ただ、好みの問題ではありますがpublicフィールドを使っているところもあります。
そのほうがコードとしてはシンプルです。
S2JDBCなんかもpublicフィールドにしています。
なので、publicフィールドも使えると便利です。


いくつか見つけた中では、
http://d.hatena.ne.jp/colun/20100102
これは一般論なので、そのままstruts2で使うわけにはいきません。
http://d.hatena.ne.jp/runfor/20090911
これはライブラリをそのまま書き換えてます。
stuts2のバージョンアップとかが面倒になるので、あまりやりたくないです。


提案したい方法は次のとおりです。
上のリンク先にあるように、やることとしては簡単に言うと

ObjectWrapper#setExposeFields(true);

とするだけです。

  • ライブラリ内のプログラムを継承して上書き
  • 設定で新しく作ったクラスを使うように変更
FreemarkerManagerを上書きする場合

クラス作成(MyFreemarkerManager)

package kamegu.common.freemarker;

import javax.servlet.ServletContext;

import org.apache.struts2.views.freemarker.FreemarkerManager;

import freemarker.ext.beans.BeansWrapper;
import freemarker.template.ObjectWrapper;

public class MyFreemarkerManager extends FreemarkerManager {
	protected ObjectWrapper createObjectWrapper(ServletContext servletContext) {
		ObjectWrapper wrapper = super.createObjectWrapper(servletContext);
		((BeansWrapper) wrapper).setExposeFields(true);
		return wrapper;
	}
}

設定(struts.xml)

(省略)
  <constant name="struts.freemarker.manager.classname" value="kamegu.common.freemarker.MyFreemarkerManager"/>
(省略)
FreemarkerResultを上書きする場合

クラス作成(MyFreemarkerResult)

package kamegu.common.freemarker;

import org.apache.struts2.views.freemarker.FreemarkerResult;

import freemarker.ext.beans.BeansWrapper;
import freemarker.template.ObjectWrapper;

public class MyFreemarkerResult extends FreemarkerResult {
	protected ObjectWrapper getObjectWrapper() {
		ObjectWrapper wrapper = super.getObjectWrapper();
		((BeansWrapper) wrapper).setExposeFields(true);
		return wrapper;
	}
}

設定(struts.xml)

(省略)
  <package ~~~~~>
    <result-types>
        <result-type name="freemarker" class="kamegu.common.freemarker.MyFreemarkerResult"/>
    </result-types>
(省略)

みんなに役立つ「テスト」を学んでみよう!

2013/10/17
DevLove関西の勉強会
http://devlove-kansai.doorkeeper.jp/events/5586
忘れないうちにメモ

そもそも

  • そういえばテストを体系的に学んだことないよな
  • 「とりあえずユニットテストやって(Web開発なので)画面チェックすればいいや」、っていう軽い考え
  • 少なくともそれくらいはやっておくべきとも思っている(ユニットテストすらやってないプロジェクトもあるので)

構成

その1:テストの必要性と品質の考え方
その2:テストの技術をざっくり紹介
その3:テスト技法紹介 ~ワークを添えて

資料については公開はまだNGとのことです。

その1

JSTQBなるもの
「試験は受けなくてもいいけど勉強することが大事」みたいなこと言ってました

http://jstqb.jp/
Advanced Levelシラバスは80ページくらい、載ってる時間は何なんだろう??
Foundation Levelシラバスも80ページくらい、学習時間の目安も書いてある

テストについて考える際には品質関連のフレームワークがヒントをくれる
ソフトウェア品質モデル(ISO9126、ISO250XX)を使って分類できる
例えば、「機能性->正確性」「信頼性->成熟性」「利用品質->生産性」とか
「もやもやしている」ものを考えたり、整理するためのきっかけにできる
「ISOとりいれてますよ~~」とか偉い人に納得してもらえやすい、のかな?
狩野モデル

http://ja.wikipedia.org/wiki/ISO_9126
ちょっと何読めばいいのかわからない。。。
とりあえずはこういう分類があるってのを抑えておくべし

妥当性確認:「適切な」ものを作っているか?
検証:「適切に」ものを作っているか?
単純に検証するだけじゃダメ、さらに上位の狙いや目的に合致していることを意識!

納得。
個人的には出来てると思う。

その2

用語だけでもいっぱい

確かに、、、そりゃ、とっつきにくいはずです
分かったつもりでいる単語も、ちゃんと理解しているかって言われると不安です

テストレベル

JSTQBでは「受入テスト」「システムテスト」「統合テスト」「コンポーネントテスト」。
SLCP2007(V字モデル)では「SW的確性確認テスト」「結合テスト」「単体テスト」。
Beizerさんの定義では「システムテスト」「統合テスト」「コンポーネントテスト」「ユニットテスト」。

いろいろあるんですね。。
普段考えているのがどのモデルに近いのかくらいはあとで確認しておこうと思います。

結合テストといったって、一気に全部まとめてエイッてわけには行かない。
階層わけるとか、段階的に。
塗り絵:段階的に「塗りつぶす」ようにテスト

段階的にってのは確かにそうですね。
普段ユニットテストしか考えて(ry
塗り絵とか積み木って話は絵がないと理解しにくいかも、というより説明しにくいです。

テストタイプ

テストをグループ化したもの
品質特性(ISO)ベース
Myersのシステムテストカテゴリ
「抜けなく」「効率的に」「状況に応じて」

http://snsk.hateblo.jp/entry/20120227/p1
紹介されてた様にマインドマップとかで1個ずつ広げていくと考えやすそうです。

その3

三色ボールペン法
同値分割
境界値分析
デシジョンテーブル
有則の組み合わせ検討技法(CEG, CFD)
無則の組み合わせ検討技法(直交表、All Pair)
状態遷移図

http://www.hayst.com/Pages/positioning.aspx
同値分割とか境界値分析は比較的わかりやすいのかなと。
有則とか無則ってのは、無則と思ってたのが有則だったりするとちょっと面倒です。
油->OK、卵->OK、お酢->OKだけど混ぜてマヨネーズにするとNGみたいな。
ちょっと分かりにくいか。。。
講義の中でも「安易にパターンを減らすことが出来る技術じゃない」って説明だったけどもう少し詳しく理解しといたほうがいいと思いました。

ワークショップ

三色ボールペン法の実践
まだ表面的な理解です。


以下、参考文献です

ソフトウェアテスト技法ドリル―テスト設計の考え方と実際

ソフトウェアテスト技法ドリル―テスト設計の考え方と実際

Googleのテスト本って言ってたのはこれかな
テストから見えてくるグーグルのソフトウェア開発

テストから見えてくるグーグルのソフトウェア開発

そのほか
ソフトウェアテストの基礎:ISTQBシラバス準拠

はじめて学ぶソフトウェアのテスト技法

ソフトウェア・テスト PRESS 総集編

マインドマップから始めるソフトウェアテスト

体系的ソフトウェアテスト入門

知識ゼロから学ぶ ソフトウェアテスト

現場の仕事がバリバリ進む ソフトウェアテスト手法


Web

http://www.jasst.jp/archives/jasst09e/pdf/A7-6.pdf