InjectableProvider
Dropwizard で利用されている Jersey-1.18 では InjectableProvider<A extends Annotation, C>
を実装した Provider を登録しておくことで、これらが提供する依存オブジェクトを取得することができます。
-
A はインジェクト対象を示すアノテーションを指定:
@Context
, etc. -
C は
java.lang.reflect.Type
またはcom.sun.jersey.api.model.Parameter
以下は Typesafe Config を提供する ConfigProvider
の例です。
import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.core.spi.component.ComponentScope;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.InjectableProvider;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;
import java.lang.reflect.Type;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
@Provider
public class ConfigProvider implements InjectableProvider<Context, Type> {
private final Config config;
public ConfigProvider() {
config = ConfigFactory.load();
}
@Override
public Injectable<Config> getInjectable(
ComponentContext ic, Context a, Type t) {
if (!t.equals(Config.class))
return null;
return new Injectable<Config>() {
@Override
public Config getValue() {
return config;
}
};
}
@Override
public ComponentScope getScope() {
return ComponentScope.Singleton;
}
}
app { version: "0.1.0" }
インジェクト対象に @Context
アノテーションを指定することで、ConfigProvider
が提供する Config
オブジェクトを取得できます。
@Path("/")
public class AppResource {
@GET
@Path("version")
@Produces(MediaType.TEXT_PLAIN)
public Response version(@Context Config config) {
return Response.ok(config.getString("app.version")).build();
}
}
AbstractHttpContextInjectable
依存オブジェクトとして、リクエストヘッダ Accept-Languages
から、ユーザの利用言語に応じたメッセージカタログを作成したいとします。以下の I18n
クラスを例にします。
import java.util.Locale;
import java.util.ResourceBundle;
public class I18n {
private static final String NAME = "messages";
private final ResourceBundle bundle;
private I18n(Locale locale) {
if (locale == null)
this.bundle = ResourceBundle.getBundle(NAME);
else
this.bundle = ResourceBundle.getBundle(NAME, locale);
}
public String get(String key) {
return bundle.getString(key);
}
public static I18n newInstance() {
return new I18n(null);
}
public static I18n newInstance(Locale locale) {
return new I18n(locale);
}
}
hello=Hello
hello=\u3053\u3093\u306B\u3061\u306F
I18n enMessages = I18n.newInstance(Locale.US);
enMessages.get("hello"); // Hello
I18n jaMessages = I18n.newInstance(Locale.JAPAN);
jaMessages.get("hello"); // こんにちは
このようにリクエストに応じた依存オブジェクトを作成したい場合、Injectable
ではリクエストヘッダを得る事ができません。代わりに jersey-server パッケージの AbstractHttpContextInjectable
を使う事で、getValue
の引数 HttpContext
から、リクエストヘッダを参照することができます。
...
import com.sun.jersey.api.core.HttpContext;
import com.sun.jersey.server.impl.inject.AbstractHttpContextInjectable;
import java.util.List;
import java.util.Locale;
@Provider
public class I18nProvider implements InjectableProvider<Context, Type> {
@Override
public Injectable<I18n> getInjectable(
ComponentContext ic, Context a, Type t) {
if (!t.equals(I18n.class))
return null;
return new AbstractHttpContextInjectable<I18n>() {
@Override
public I18n getValue(HttpContext ctx) {
List<Locale> locales = ctx.getRequest().getAcceptableLanguages();
if (locales.isEmpty())
return I18n.newInstance();
else
return I18n.newInstance(locales.get(0));
}
};
}
@Override
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}
}
リクエストごとにインスタンスを生成する必要があるので、スコープは CompoentScope.PerRequest
とし、メゾッドインジェクションでのみ取得します。
@GET
@Path("hello")
@Produces(MediaType.TEXT_PLAIN)
public Response hello(@Context I18n messages) {
return Response.ok(messages.get("hello")).build();
}
この方法により、リクエスト情報からメッセージカタログを組み立てる同ロジックを各リソースクラスに散りばめる必要がなくなります。
Dropwizard 公式マニュアルの Authentication の章で、 @Auth
アノテーションを用いた認証ユーザの取得方法が紹介されていますが、BasicAuthProvider
/ OAuthProvider
ともに AbstractHttpContextInjectable
が使われています。