2011-11-04 57 views
1

這是我的場景:我可以訪問Hibernate事件偵聽器中的FacesContext嗎?

我有my-jsf-app.war(JSF 1.2應用程序)和daos-and-entities.har(Hibernate 3.3.1)部署到JBOSSAS 5服務器。我想要的是在我的應用程序中有某種更改日誌,其中包含「誰進行了更改」的信息。所以我的想法是讓hibernate事件監聽器訪問FacesContext(或者實際上在上下文中的HttpSession)來檢索當前登錄的用戶。

所以基本上我配置了事件偵聽器並添加了以下方法來檢索當前登錄的用戶。

protected static String tryToFindUserLogin() { 

    try { 
     Class classFacesContext = Class.forName("javax.faces.context.FacesContext"); 
     Class classExternalContext = Class.forName("javax.faces.context.ExternalContext"); 
     Method methodGetCurrentInstance = classFacesContext.getMethod("getCurrentInstance", (Class[])null); 
     Method methodGetExternalContext = classFacesContext.getMethod("getExternalContext", (Class[])null); 
     Method methodGetRemoteUser = classExternalContext.getMethod("getRemoteUser", (Class[])null); 

     // This call always returns null 
     Object currentFacesContext = methodGetCurrentInstance.invoke(null, (Object[])null); 

     Object currentExternalContext = methodGetExternalContext.invoke(currentFacesContext, (Object[])null); 
     String login = (String) methodGetRemoteUser.invoke(currentExternalContext, (Object[])null); 
     logger.debug("Found Weblogin: " + login); 
     return login; 

    } catch (Exception e) { 
     logger.debug(e, e); 
    } 

    return "anonymous"; 
    } 

我使用java反射來做到這一點,因爲HAR文件也用於其他非jsf項目。

問題是我從來沒有得到currentFacesContext。我明白FacesContext.getCurrentInstance()是ThreadLocal。但是,如果在JSF ManagedBean action方法中直接觸發hibernate操作(例如saveOrUpdate()),那麼事件監聽器不應該與action方法執行相同的線程嗎?

任何幫助都很有價值,也許對於我的場景還有一種完全不同的方法,我不知道。我顯然不想爲我所有的dao調用添加一個userContext參數,因爲這將是一項大量的工作。提前

+0

我已經看到了與EclipseLink和JSF類似的工作,所以它應該是可能的。 嘗試使用調試器進行單步調試 - 在事件監聽器上設置斷點並查看堆棧跟蹤的外觀。這至少會證實你對範圍的假設(這似乎是合理的),事件偵聽器將在JSF請求的範圍內運行,以便FacesContext被綁定。 其他要嘗試的事情:使用常規方法調用而不反射;擺脫「靜態」;或者將監聽器放入web應用程序本身。也許一些微妙的類加載器層次結構問題。 – wrschneider

+0

感謝提示類加載器問題,這導致我找到了解決方案。我會發佈一個答案。 – huo73

回答

1

得到一個暗示居停後

THX,我找到了解決辦法。 WAR在其WEB-INF \ lib中包含JSF實現JAR。由於WAR和HAR是獨立的部署,因此它們使用單​​獨的類加載器,這會導致WAR從Web-INF \ lib加載JSF實現,而HAR會從JBOSS安裝加載它。

我能夠通過以下方式改變上面的代碼來強制使用正確的類加載器:

protected static String tryToFindUserLogin() { 

    try { 
     /*** Obtain the classLoader from the calling Thread, instead of using the default ***/ 
     ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 
     Class classFacesContext = classLoader.loadClass("javax.faces.context.FacesContext"); 
     Class classExternalContext = classLoader.loadClass("javax.faces.context.ExternalContext"); 
     /**************/ 

     Method methodGetCurrentInstance = classFacesContext.getMethod("getCurrentInstance", (Class[])null); 
     Method methodGetExternalContext = classFacesContext.getMethod("getExternalContext", (Class[])null); 
     Method methodGetRemoteUser = classExternalContext.getMethod("getRemoteUser", (Class[])null); 

     Object currentFacesContext = methodGetCurrentInstance.invoke(null, (Object[])null); 

     Object currentExternalContext = methodGetExternalContext.invoke(currentFacesContext, (Object[])null); 
     String login = (String) methodGetRemoteUser.invoke(currentExternalContext, (Object[])null); 
     logger.debug("Found Weblogin: " + login); 
     return login; 

    } catch (Throwable e) { 
     logger.debug(e, e); 
    } 

    return "anonymous"; 
    } 

此外,我改變了catch塊捕捉(Throwable的),因爲如果在非JSF運行環境getMethod()可能會拋出一個RuntimeException,它不會被捕獲(例外)

這完美的作品!

+0

感謝Huo73。澄清異常部分的一點:異常的捕獲子句DOES CATCH RuntimeException。由於它是異常的子類... http://stackoverflow.com/questions/12951236/if-runtimeexception-is-thrown-can-it-be-caught-as-an-exception – devBinnooh

相關問題