5

GlassFish 3 の Java EE JPA (2.0) アプリケーションに何らかの監査を実装しようとしています。

エンティティに@EntityListeners注釈を追加しました。リスナーには、実行時に正常に呼び出されるメソッドにand注釈があります。@MappedSuperclass@PrePersist@PreUpdate

これらのメソッドでは、現在のユーザーの ID を取得するために( @Inject) a @Named, @Stateful, @SessionScopedbean ( )を使用しようとしています。UserSessionリスナー クラスには注釈がまったくありません。

UserSession問題は、 Bean を注入できないことです。私はいつもnull値で終わります。今回は、常に null 値を注入するプレーン を試しました。また、常に新しいオブジェクトを返すものも試しました (コンストラクター呼び出しを確認し、オブジェクトは空です)。@Inject UserSession us;UserSession us = (UserSession) ctx.lookup("java:global/application/module/UserSession");

CDIに関して非常に重要なことを見逃していると確信していますが、何がわかりません。誰かが私を正しい方向に向けてもらえますか?

4

3 に答える 3

2

少なくとも JPA 2.0 では、EntityListner は CDI をサポートしていません。どうやらJPA 2.1の新機能のリストに載っているようです

私もこれに出くわしたときは驚きました。

于 2012-07-06T19:54:35.570 に答える
1

@Stateful最終的に、 Beanの参照を取得できる回避策を見つけました。

Beanの参照を@Named @Singleton @Startup持つローカルを保持する Bean SessionControllerを作成しました。HashMap<String, UserSession> sessionMap@Stateful

@Named
@Singleton
@Startup
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class SessionController {

private HashMap<String, UserSession> sessionMap;

@PostConstruct
void init() {
    sessionMap = new HashMap<String, UserSession>();
}

@PreDestroy
void terminate() {
    for (UserSession us : sessionMap.values()) {
        us.logoutCleanUp(); //This is annotated as @Remove
    }
    sessionMap.clear();
}

public void addSession(String sessionId, UserSession us) {
    sessionMap.put(sessionId, us);
    System.out.println("New Session added: " + sessionId);
}

public UserSession getCurrentUserSession() {
    FacesContext context = FacesContext.getCurrentInstance();
    String sessionId = ((HttpSession) context.getExternalContext().getSession(false)).getId();
    return sessionMap.get(sessionId);
}

}

各 Bean の@PostConstructメソッド内から参照を追加します。

public class UserSession implements Serializable {
@Inject SessionController sc;
...
    @PostConstruct
    void init() {
    FacesContext context = FacesContext.getCurrentInstance();
    String sessionId = ((HttpSession) context.getExternalContext().getSession(true)).getId();
    sc.addSession(sessionId, this);
}

.getSession(true)セッションがまだ作成されていない可能性があるため、これが必要であることに注意してください。はコンストラクタではないthisため、安全に渡されることにも注意してください...@PostConstruct

これらすべての後、次のように EntityListener (およびその他の場所) で参照を取得できます。

SessionController sc = (SessionController) new InitialContext().lookup("java:module/SessionController");
    UserSession us = sc.getCurrentUserSession();

またはCDI Beanでこのように

@Inject SessionController sc;

FacesContext context = FacesContext.getCurrentInstance()私が見る唯一の欠点は、このアプローチが Web アプリケーション (意味のある場所) でのみうまく機能することです。一部の Bean (および最終的には EntityListeners) も@javax.jws.WebServiceas @StatelessBean を介して公開されます。このコンテキスト (実際には: が存在しない) では、いかなる種類のsessionIdも存在しないため (正確にはセッションがまったく存在しないため)、私のシングルトンは機能しません (まだテストしていません)。おそらくBeanのSessionContextを使用するか、何らかの使用可能なsessionIdを発明することで、これを回避する必要があります。使いやすいものを作成したら投稿します...

于 2012-07-07T15:05:09.617 に答える
1

今回は、プレーンな @Inject UserSession us; を試しました。これは常に null 値を挿入します。

これは、JPA 2.0 ではクラスが CDI によって管理されないため、@Inject はそれらで機能しません。Steve K. が指摘したように、これらのクラスは JPA 2.1 以降の CDI によって管理されます。

UserSession us = (UserSession) ctx.lookup("java:global/application/module/UserSession"); も試しました。

JNDI で CDI によってインスタンス化された Bean を検索することはできません。ただし、できることは、JNDI で CDI BeanManager を検索し、BeanManager から Bean を取得することです。CDI の仕様により、アプリケーションの BeanManager は常に「java:comp/BeanManager」にあることが保証されています。簡単な例を次に示します。

InitialContext ctx = new InitialContext();
BeanManager bm = ctx.lookup("java:comp/BeanManager");
Set<Bean<?>> beans = bm.getBeans(UserSession.class);
Bean<?> bean = bm.resolve(beans);
CreationalContext<?> ctx = bm.createCreationalContext(bean);
UserSession us = (UserSession) bm.getReference(bean, UserSession.class, ctx);

セッション スコープの UserSession インスタンスの場合、これは CDI によって管理される Bean になります。

私の答えは、EntityListeners の CDI インジェクションで提案された内容に基づいていました。

于 2013-10-09T12:19:34.880 に答える