2

簡単な質問:トランザクションとリソースインジェクションがPOJOで機能するように、ステートレスEJBによって呼び出されたPOJOをEJBのコンテキストで動作させる方法はありますか?

具体的には、私がやろうとしていることのコンテキストで:POJOを呼び出してメッセージを送信する前に、データベース内の一部のデータを保持するEJBのトランザクションにPOJO JMSプロデューサーを含めるにはどうすればよいですか?例外が原因で送信された場合、データベーストランザクションもロールバックされますか?メールを非同期で送信したい。

これは幸せな道です(ステートレスセッションBean内で開始):

  • データベースにデータを保存する//これは機能する
  • 永続化されたデータから選択データをプルし、それをカスタムの「メッセージ」クラス(実際にはdto)に配置します
  • EmailQueueMessenger POJOのsendEmailメソッドを呼び出して、メッセージオブジェクトを渡します。
  • メッセージはMDBに送信され、電子メールを処理して送信します(質問の一部ではなく、完全を期すためにここにあります)

以下のコードは機能します。たとえば、コンテキストルックアップでエラーを強制した場合、呼び出し元のクラスでデータベースが「永続化」することはありません。ところで、@Resourceインジェクションも機能させることができません。

//In the EJB
EmailQueueMessenger eqm = new EmailQueueMessenger();
eqm.sendEmail(messageObject);
// mailObject will be translated into an email message at the other end of the queue.  

/******************** POJO Below ************/  

public class EmailQueueMessenger implements Serializable {

    // Resource injection doesn't work... using 'lookup' below, which does work.
    //    @Resource(name = "jms/EmailerQueueConnectionFactory")
    //    private ConnectionFactory connectionFactory;
    //    @Resource(name = "jms/EmailerQueue")
    //    private Destination EmailerQueue;

        public EmailQueueMessenger() {
        }

        public void sendEmail(MailMessageDTO theMessage) {

            Context ctx = null;
            try {
                ctx = new InitialContext();
                ConnectionFactory connectionFactory = (ConnectionFactory) ctx.lookup("jms/EmailerQueueConnectionFactory");
                System.out.println("JMS Producer CTX Name In Namespace: " + ctx.getNameInNamespace());
                //Destination EmailerQueue = (Destination) ctx.lookup("jms/ERROR"); // forces exception
                Destination EmailerQueue = (Destination) ctx.lookup("jms/EmailerQueue");  // normal working code

                try {
                    Connection con = connectionFactory.createConnection();
                    Session session = con.createSession(false,
                            Session.AUTO_ACKNOWLEDGE);
                    MessageProducer msgProd = session.createProducer(EmailerQueue);

              ...

追加してみました:

@TransactionAttribute(TransactionAttributeType.MANDATORY)
@Stateless

POJOの定義になりますが、違いはありません。

FWIW私はEmailQueueMessengerに別のクラスを使用しています。これは、アプリの他の部分で時折メールを送信する必要があるため、コードを複製したくないためです。


すべてのJMSのものを最初のEJB内に移動し、正しく実行されたテストを行ったことに言及する必要があります...しかし、アプリの他の部分で使用するために、これを別のクラスで機能させる必要があります。

4

1 に答える 1

1

私はあなたが2つの問題を抱えていると思います:

  1. pojoをSLSBにする必要があります。プロキシ参照を処理するために、直接呼び出されるのではなく、jmsリスナーに挿入する必要があります。コンテナにデプロイされていない場合、注釈は無視されるため、単純なpojoとして再利用できます。

  2. AUTO_ACKNOWLEDGEを使用してjmsセッションを作成していますが、トランザクションを実行する必要があります。さらに、jms接続がトランザクションJCAソースからのものであることを確認してください。これにより、セッションがトランザクションに関連付けられます。

=========更新=========

ちょっとビル;

申し訳ありませんが、外部Beanは何らかの理由でJMSリスナーだと思いました.....とにかく、問題は同じです。

EmailQueueMessengerを、配置したアノテーション(トランザクション、インジェクションなど)に従って動作させる場合は、単純なpojoとしてではなく、EJBとして参照する必要があります。したがって、外部セッションBeanは次のようになります。

@EJB   // key difference
private EmailQueueMessenger eqm;

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void sendMessage(Object messageObject) {
   eqm.sendEmail(messageObject);
}

今あなたの

@Resource(name = "jms/EmailerQueueConnectionFactory")
@Resource(name = "jms/EmailerQueue")

@TransactionAttribute(TransactionAttributeType.MANDATORY)
@Stateless

注釈が尊重されます。

最後に、JMS送信者は呼び出しの時点でトランザクションに登録され、トランザクションマネージャーがトランザクションに2番目のリソースマネージャー(最初はDB、次にJMS)を参加させていることを認識していることを確認する必要があります。Glassfishについてはあまり詳しくありませんが、接続ファクトリのトランザクションサポートのレベルを指定できるスイッチ付きの設定画面があるようです。

送信者コードを次のように変更します。

Session session = con.createSession(true, Session.SESSION_TRANSACTED);

技術的には、JMS接続インスタンスをEmailQueueMessengerインスタンスにキャッシュできます。トランザクションが完了したときに処理されるため、コードでJMSセッションを閉じないでください(ただし、この時点でJMS / JTAの実装間に差異が見られます)。

私はそれがそれをクリアすることを願っています、そして私はそれがうまくいくことを本当に望んでいます!

于 2012-05-03T22:01:31.560 に答える