Tomcat 6 で実行される jersey 1.13 と spring 3.1.1 を使用してレスト サービスを作成しました。Tomcat では、認証を行うレルムを使用しています。私のアプリケーションでは現在のユーザーが必要ですが、すべてのリソースで jersey から SecurityContext にアクセスしたくありません。現在のユーザーを含む残りのリソースに、リクエスト スコープの ApplicationConfig オブジェクトを挿入したいと考えています。後で、このクラスを拡張して、より多くの要求レベルの構成パラメーターを含めることができます。これは私には素晴らしい抽象化のようです。
@Component
@Scope(value = "request")
public class ApplicationConfig
{
private String userCode;
public String getUserCode()
{
return this.userCode;
}
public void setUserCode(String userCode)
{
this.userCode = userCode;
}
}
構成へのアクセスを提供する ApplicationConfigManager を作成しました。
@Component
public class ApplicationConfigManager
{
@Autowired
public ApplicationConfig applicationConfig;
public ApplicationConfig getApplicationConfig()
{
return this.applicationConfig;
}
}
アプリケーション構成マネージャーはシングルトン (デフォルト) として定義されていますが、ApplicationConfig はリクエスト スコープである必要があるため、@Scope アノテーションが付けられています。
(ジャージー)ContainterRequestFilterを使用して、アプリケーション構成オブジェクトにユーザーを設定しています。
@Component
@Provider
public class ApplicationConfigFilter implements ResourceFilter, ContainerRequestFilter
{
@Autowired
private ApplicationConfigManager applicationConfigManager;
@Override
public ContainerRequest filter(ContainerRequest request)
{
this.applicationConfigManager.getApplicationConfig().setUserCode(
request.getSecurityContext().getUserPrincipal().getName()
);
return request;
}
@Override
public ContainerRequestFilter getRequestFilter()
{
return this;
}
@Override
public ContainerResponseFilter getResponseFilter()
{
return null;
}
}
このフィルターを登録するために、ResourceFilterFactory を作成しました
@Component
@Provider
public class ResourceFilterFactory extends RolesAllowedResourceFilterFactory
{
@Autowired
private ApplicationConfigFilter applicationConfigFilter;
@Override
public List<ResourceFilter> create(AbstractMethod am)
{
// get filters from RolesAllowedResourceFilterFactory Factory!
List<ResourceFilter> rolesFilters = super.create(am);
if (null == rolesFilters) {
rolesFilters = new ArrayList<ResourceFilter>();
}
// Convert into mutable List, so as to add more filters that we need
// (RolesAllowedResourceFilterFactory generates immutable list of filters)
List<ResourceFilter> filters = new ArrayList<ResourceFilter>(rolesFilters);
filters.add(this.applicationConfigFilter);
return filters;
}
}
これをweb.xmlに設定して、このファクトリをアクティブにしました
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.mypackage</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>com.mypackage.ResourceFilterFactory</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
また、これらのリスナーを追加して、スプリング コンテキストをブートストラップし、リクエスト スコープを機能させました。
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
これは、スキャン機能を機能させ、アプリケーション構成オブジェクトを定義するためにアプリケーションコンテキストに入れたものです(Springが自動的に見つけるので、これは必要でさえないと思います)
<context:annotation-config/>
<context:component-scan base-package="com.mypackage" />
<bean id="applicationConfig" class="com.mypackage.ApplicationConfig" scope="request"/>
そして今、私の問題。アプリケーション スプリングを起動すると、ブートストラップされ、ApplicationConfig オブジェクトが ApplicationConfigFilter に注入される ApplicationConfigManager に注入されます。
この時点で、例外がスローされます。
.
.
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually o
perating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) ~[spring-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at org.springframework.web.context.request.AbstractRequestAttributesScope.get(AbstractRequestAttributesScope.java:40) ~[spring-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:328) ~[spring-beans-3.1.1.RELEASE.jar:3.1.1.RELEASE]
... 57 common frames omitted
この例外は非常に明確であり、リクエストがまだ送信されていないため、リクエストスコープの ApplicationConfig を注入できないことを意味していると思います。
したがって、アプリケーションの起動時ではなく、リクエストが送信されたときにのみ ApplicationConfig オブジェクトをインスタンス化する必要があります。解決策を探したところ、シングルトン Bean にリクエスト スコープ Bean を注入することはあまり論理的ではないことがわかりました。とにかく、私は常に同じオブジェクトを取得します。解決策はプロキシを使用することなので、 ApplicationConfig クラスの @Scope アノテーションをこれに変更しました
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
これにより、リクエストごとに新しい ApplicationConfig オブジェクトが提供されるはずです。
アプリは正常に起動するようになりました (ApplicationConfig をインスタンス化していないようです) が、残りのサービスにリクエストを送信すると、デバッグを使用して、リクエストに対して同じオブジェクトではなく異なる ApplicationConfig オブジェクトを取得することがわかりました。
それで、私は何を間違っていますか?