2012-12-12 22 views
4

如何處理mdb中的異常?我有一種有趣的感覺,那就是try catch塊之後發生異常,所以我無法捕獲並記錄它。 Glassfish v3決定重複整個消息。它運行到一個無限循環,並在硬盤上寫入很多日誌文件。消息驅動Bean(MDB)中的捕獲異常

我使用Glassfishv3.01 +的EclipseLink 2.0.1

public class SaveAdMessageDrivenBean implements MessageListener { 

    @PersistenceContext(unitName="QIS") 
    private EntityManager em; 

    @Resource 
    private MessageDrivenContext mdc; 

    public void onMessage(Message message) { 
     try { 
      if (message instanceof ObjectMessage) { 
       ObjectMessage obj = (ObjectMessage)message; 
       AnalyzerResult alyzres = (AnalyzerResult)obj.getObject(); 
       save(alyzres); 
      } 
     } catch (Throwable e) { 
      mdc.setRollbackOnly(); 
      log.log(Level.SEVERE, e); 
     } 
    } 

    @TransactionAttribute(TransactionAttributeType.REQUIRED) 
    private void save(AnalyzerResult alyzres) throws PrdItemNotFoundException { 

     Some s = em.find(Some.class, somepk); 
     s.setSomeField("newvalue"); 

     // SQL Exception happens after leaving this method because of missing field for ex. 
    } 
}  

回答

5

你有消息中毒不好的情況下...

我看到的主要問題是:

  • 你在你的onMessage()直接save()方法調用:這意味着泰德的容器具有沒有辦法注入適當的交易處理代理周圍的save方法
  • 在任何情況下save()方法應該有@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)爲了提交一個單獨的事務,否則它會加入onMessage事務(默認爲REQUIRED),並繞過您的異常處理代碼的onMessage

成功執行後beign犯了什麼我woud做的是:

移動save方法到一個新的無狀態會話bean:

@Stateless 
public class AnalyzerResultSaver 
{ 
    @PersistenceContext(unitName="QIS") 
    private EntityManager em; 

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    private void save(AnalyzerResult alyzres) throws PrdItemNotFoundException { 
     Some s = em.find(Some.class, somepk); 
     s.setSomeField("newvalue"); 
     // SQL Exception happens after leaving this method 
    } 
} 

進樣這個bean在MDB:

public class SaveAdMessageDrivenBean implements MessageListener { 

    @Inject 
    private AnalyzerResultSaver saver; 

    @Resource 
    private MessageDrivenContext mdc; 

    public void onMessage(Message message) { 
     try { 
      if (message instanceof ObjectMessage) { 
       ObjectMessage obj = (ObjectMessage)message; 
       AnalyzerResult alyzres = (AnalyzerResult)obj.getObject(); 
       saver.save(alyzres); 
      } 
     } catch (Throwable e) { 
      mdc.setRollbackOnly(); 
      log.log(Level.SEVERE, e); 
     } 
    } 
} 

另一個提示:在這段代碼中,郵件中毒依然存在。現在它來自調用mdc.setRollbackOnly();的行。

我建議在這裏記錄異常並將消息轉移到毒性隊列,從而阻止容器重新提交消息並且無限。

UPDATE:

A「有害隊列」或「錯誤隊列」是一個簡單的平均值,以保證您的(希望可恢復)丟棄的消息不會完全喪失。它在集成場景中大量使用,其中消息數據的正確性無法保證。

設置一個毒性隊列意味着定義一個目標隊列或主題,並將「壞」消息重新傳遞到這個目的地。

週期性地,操作者要檢查一下隊列(通過專用的應用程序)和任一修改的消息並重新提交到「好」隊列,或丟棄該消息並要求resumbit。

+0

非常感謝,我該如何「將信息傳遞給一個毒藥隊列」? –

+0

你的想法很好,謝謝了很多 –

+0

@HananTuncay澄清了'毒素隊列部分',我不確定它是因爲中毒來源於'編程'錯誤,這裏的日誌應該足夠了,所以簡單地省略'mdc.setRollbackOnly();' –

1

如果我沒有記錯的話,你讓容器處理事務。通過這種方式,實體管理器將對方法結束後將被刷新的操作進行排隊,這就是爲什麼在方法結束後你有異常。

直接使用em.flush()作爲該方法的最後一步將執行事務的所有相關查詢,並在此處拋出異常,而不是稍後在容器創建flush()時提交事務。

+0

是否平齊()只犯我這樣一個MDB中所做的更改或者它刷新各種來自其他多邊開發銀行在同一時間運行的事務的。那麼在使用flush時,entitymanager的重點是什麼? –

+0

我該如何阻止無限循環? –

+0

'flush()'方法將對象的實際持久性帶入方法(和當前事務)的範圍。通過圍繞它與'try-catch'塊,你應該能夠捕捉異常並處理它們。 – Gamb

2

我相信你已經發布的代碼主要是確定。

您使用的

@TransactionAttribute(TransactionAttributeType.REQUIRED) 

完全被忽略,因爲這(和大多數其他)註釋只能適用於商業方法(包括的onMessage)。這並不重要,但是因爲你的onMessage方法獲得了一個免費的隱式方法。

這導致這樣的事實,消息處理是在Java EE容器事務。如果交易由於任何原因而失敗,那麼容器需要再次嘗試並傳遞消息。現在

,你的代碼是從追趕保存方法,這是很好的例外。但是,你明確地標記了回滾事務。這具有告訴容器消息傳遞失敗並且應該再次嘗試的效果。

因此,如果您刪除:

mdc.setRollbackOnly(); 

容器將停止嘗試重新傳遞消息。