13013


JAX-RSを使ったWebアプリケーションを作っています。  
下記の条件を全て満たすクラスからセッションにアクセスできるようにしたいのですが、方法が分かりません。

  1. Beanではない任意の自作クラスである
  2. 引数によってセッションにアクセスするオブジェクト（例えばHttpServletRequest）を入手できない

ASP.NETの場合、`HttpContext.Current`というstaticプロパティを経由して、いつでもセッション（やその他のHTTP関連オブジェクト）にアクセスできます。  
Java(JAX-RS)で同じような手段はありますか？

やろうとしていることは色々あるのですが、例えば「ログインユーザのIDをデータアクセス層の入り口（各プログラマに対して隠蔽された共通基盤内部）でロギングする」といったものです。  
ログインユーザの情報はセッションで保持しているのですが、この「共通基盤」と呼んでいるものは、 **JAX-
RSのWebサービスとはクラス間の関連としては全くつながっていません** 。  
つまり、同じアプリケーション上に存在するクラスではありますが、クラス間依存の連なりとか、そういう意味での繋がりは全くありません。  
ですので、`HttpContext.Current`のように、グローバル変数のようなAPIがあれば助かります。

`@SessionScoped`でDIを使う方法も考えられますが、これだと隠蔽された共通基盤の中に組み込めないように思います。

あるいは、WebサービスのRequestFilterで（もっと適したものがある？）セッションから情報を取り出し、どこか共通クラスのstaticなフィールドにコピーするという考えも一瞬浮かびましたが、全くスレッドセーフではないのでNGでした。  
ThreadLocalを使えば良いのかもしれませんが、ちょっと知識・経験不足で安全に実装できるかどうか、不安です。  
他に手段が無ければ色々と調査・検証した上で導入する必要がありますが・・・

何か良い方法は無いでしょうか？

環境は以下の通りですが、例えば「Spring Frameworkなら可」であれば、導入することも考えます。  
アプリケーションサーバ：WildFly  
使用フレームワーク：JavaEE7、Jersey(JAX-RS)


CC BY-SA 3.0
2015-07-31T04:07:10.953
12866
2015-08-05T14:00:25.577
2015-08-03T17:10:42.660
8078
8078
question
java jax-rs java-ee
JAX-RSを使ったWebアプリ上で非Beanクラスからセッションにアクセスしたい


[@Context](https://jax-rs-
spec.java.net/nonav/2.0/apidocs/javax/ws/rs/core/Context.html) アノテーションを使って
[HttpServletRequest](https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html)
オブジェクトをインジェクトすることが可能です。

次のようにしてリソースメソッドのパラメータに `HttpServletRequest` オブジェクトを渡すことができます。

[code]

    @Path("/contacts")
    public class ContactService {
        @GET
        @Path("{id}")
        @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
        public Contact find(@Context HttpServletRequest request, @PathParam("id") Long id) {
            return repository.find(id);
        }
    }
    
[/code]

あとはこのメソッド内で
[HttpServletRequest#getSession()](https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getSession--)
メソッドをコールして `HttpSession` オブジェクトを取得すれば良いでしょう。


CC BY-SA 3.0
2015-07-31T06:40:01.647
12872
2015-07-31T06:40:01.647
3374
12866
answer


コメントで何度も尋ねてすみませんでした。ようやく前提条件が理解できたので問題を整理しました。  
なお結論的には @AOE Takashi さんがコメントでおっしゃっているのと変わりませんが、CDIによる方法の方がよりお勧めです。

### 質問の前提条件：

  1. 「共通基盤」は別アプリなどではない単なるモジュールであり、開発ターゲットアプリにライブラリの形で組み込まれる。
  2. 共通基盤モジュールはターゲットアプリのhttpリクエスト処理中にモデル層において呼び出される。その意味においてターゲットアプリから共通基盤モジュールへの依存は存在する。

### 実現したい要求：

  1. httpリクエストスコープに紐づく情報を共通基盤モジュールで取得したい。ただし、 
    * ターゲットアプリから共通基盤モジュールを呼び出す際に、明示的にパラメータを渡したくない。
    * 共通基盤モジュールではJavaEEフレームワークへの依存をしたくない。

→つまり、共通基盤モジュールからターゲットアプリへの依存の発生を回避したい。

### 検討：

引数によるパラメータを介さずにモジュール間で情報を渡すには、実装方針としては大きく分けて

  1. 大域変数（あるいはインスタンス）を介して渡す
  2. DIコンテナによってインスタンスをインジェクションしてもらう
  3. 非同期処理であるならメッセージキューイングを利用する

になるのではないかと思います。しかしいずれの方針であっても共通基盤モジュールから外部への依存は避けられません。ASP.NETの利用経験がないので誤解しているかもしれませんが、`HttpContext.Current`は考え方としては上記の１番目に該当するものと思います。残念ながらJavaEEにおいてはあらかじめ用意されたそのようなものはないはずです。Springを使ったところで共通基盤から外部への依存がなくせるわけでもありません。

今回の前提条件によれば共通基盤モジュールの処理は非同期処理でない（httpリクエスト処理スレッドで呼び出される）ため、３番目の方針は除外されます。

### 回答：

１番目の方針を採るなら ThreadLocal
を利用した実装となるでしょう。ただすでに懸念をお持ちのように、簡単にメモリーリークの原因になってしまうため、アプリサーバーで利用するには細心の注意が必要です。こちらの場合は共通基盤モジュールから外部への依存が発生します。

２番目の方針を採るなら CDI を利用することになります。今回の場合だと`@Inject
HttpServletRequest`でインスタンスを注入する先を共通基盤モジュールではなく「共通で使うDBアクセスAPI」あたりにすれば、共通基盤モジュールから外部への依存は回避できます。そこから共通基盤モジュールへはパラメータで渡す（Mapに詰め替えればより汎用的）しかないと思います。もちろんこちらの場合でもDBアクセスAPIの呼び出しに至るまでにセッション情報を持ちまわる必要もありません。

別解としてオブザーバーパターンが使えないかとも考えましたが、今回の目的にはそぐわないだろうと判断しました。

### 追記：

似たような議論を本家SOで見つけましたのでリンクだけ貼っておきます。  
[servlets - Is there a request context in Java? - Stack
Overflow](https://stackoverflow.com/questions/10197046/is-there-a-request-
context-in-java)


CC BY-SA 3.0
2015-08-04T04:33:38.003
13013
2015-08-05T14:00:25.577
2017-05-23T12:38:55.250
-1
10492
12866
answer