精通 Grails: 在企業中使用 Grails

火星人 @ 2014-03-12 , reply:0
←手機掃碼閱讀

  
在本期的 精通 Grails 中,作者 Scott Davis 將解釋為何 Grails 已經可以在企業中使用。您將看到如何將 Grails 與企業級庫結合使用,包括 Java™ 管理擴展(Java Management Extensions,JMX)、Spring 和 log4j。

常常有人問我 Grails 是否已經可以在企業中使用。簡單的回答是 “是”。而我通常給出更加詳細的回答:“只要您覺得 Spring 和 Hibernate(Grails 所依賴的底層技術)已經就緒;只要您覺得 Tomcat 或 JBoss(或 Java 企業版[Java EE])應用伺服器已經就緒;只要您覺得 MySQL 或 PostgreSQL(或者您使用的資料庫)已經就緒;只要您覺得 Java 編程已經企業就緒,那麼 Grails 就已經企業就緒” 。

British Sky Broadcasting Group 最近將它的 Web 站點遷移到了 Grails。他們現在每月的點擊量達到 1.1 億次。LinkedIn.com 在其站點的某些商業部分使用 Grails。Tropicana Juice 在英國有一個 Web 站點,該站點幾年來一直在 Grails 上運行。Grails.org 本身就是用 Grails 編寫的,每月支持 70,000 多次下載。而 SpringSource 最近有關 G2One(Groovy 和 Grails 所在的公司)的問卷調查結果完全可以打消 Groovy 和 Grails 是否適合企業使用的任何疑慮。

Groovy 有時候看起來比較奇怪,最重要的是要記住,它完全是用普通的 Java 代碼實現的。儘管 Grails 開發與其他典型的 Java Web 框架看起來很不一樣,但最終您仍然會得到一個與 Java EE 兼容的 WAR 文件。

在這篇文章中,您將探討一些用於監控和配置的企業級工具。學習如何使用 JMX 調整 Grails 應用程序。本文將簡要介紹 Grails 中的 Spring 配置。您還會看到如何在 Config.groovy 中首次指定 log4j 設置,以及如何使用 JMX 動態調整它們。

關於本系列

Grails 是一種新型 Web 開發框架,它將常見的 Spring 和 Hibernate 等 Java 技術與當前流行的配置優於約定等實踐相結合。Grails 是用 Groovy 編寫的,它可以與遺留 Java 代碼無縫集成,同時又加入了腳本編程語言的靈活性和動態性。學習完 Grails 之後,您將徹底改變看待 Web 開發的方式。

實現 JMX 工具

JMX 是 2000 年推出的。更確切地說,它是最古老的 JSR 之一 — JSR 3。隨著 Java 語言在伺服器上越來越流行,遠程優化和配置實時運行應用程序成為平台的關鍵部分。在 2004 年,Sun 使用 JMX 實現了 JVM 並推出了支持工具,比如針對 Java 1.5 JDK 的 JConsole。

JMX 通過一個統一的介面提供 JVM 內省機制、應用伺服器和類。這些不同的組件通過受管 bean(簡寫為 MBean)呈現給管理控制台。

MBeans 就像汽車儀錶板上的各種儀錶、刻度盤和開關。有些儀器是只讀的,就像速度計一樣;有些儀器是 “可寫的”,就像加速器一樣。但 MBean 是遠程管理工具,所以這個儀錶板比喻不是很不恰當。可以將其想象為遠程打開汽車的轉向燈或改變車裡的電台頻道。

啟用本地 JMX 代理

本地還是遠程?

對開發和測試而言,在本地同時運行 JMX 代理和客戶機通常是最簡單的事情。但在實際生產環境中遠程監控代理時,JMX 的好處就會凸顯出來。JConsole 與其他任何 Java 進程一樣佔用系統資源(RAM、CPU 周期等)。這會出現問題,特別是監控的生產伺服器的負載壓力較大時。但更重要的是,能夠從一個地方監控多台伺服器將使您成為數字領域的佼佼者。

當然,遠程監控生產伺服器還可以恰當保護它們的安全。您可以設置密碼保護或使用更好的公/私鑰身份驗證(請參閱 參考資料)。

要使用 JMX 進行監控,則必須先啟用它。在 Java 5 中,您必須在運行時為 JVM 提供幾個與 JMX 相關的標誌(在 Java 6 中,這些設置已經就緒,不過您一定要自己設置的話,也是可以的)。在 JMX 中,要設置一個 JMX 代理。清單 1 顯示了 JVM 參數:


清單 1. 啟用 JMX 監控的 JVM 參數
				  -Dcom.sun.management.jmxremote   -Djava.rmi.server.hostname=localhost  

一些教程建議創建一個全局 JAVA_OPTS 環境變數來保存 JMX 標誌。其他教程則建議在命令行輸入標誌:java -Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=localhost someExampleClass。

兩種方法都是可行的,但是對生產環境而言它們都不是最好的。我發現最好的方法是在伺服器的啟動腳本中設置這些值。如果每次重新啟動伺服器時都要輸入這些複雜的標誌,則表明這是一個不好的解決方案。應避免設置 CLASSPATH 和 JAVA_OPTS 等全局變數,原因有兩個:在複製伺服器(在伺服器之間複製一個一致的啟動腳本更容易)時增加了不必要的配置步驟,而且它們強制同一機器上的所有 Java 進程共享同一配置。是的,您可以創建一個詳細的清單來提醒您這些瑣碎的配置細節,但是記錄複雜的東西遠不如將複雜去掉有效。

對於 UNIX®、Linux® 和 Mac OS X 系統,Grails 啟動腳本是 $GRAILS_HOME/bin/grails。編輯這個文件,添加兩個 JAVA_OPTS 行,如清單 2 所示:


清單 2. 在 Grails 啟動腳本中為 UNIX、Linux 和 Mac OS X 啟用 JMX 監控
				  #!/bin/sh  DIRNAME='dirname "$0"'  . "$DIRNAME/startGrails"    export JAVA_OPTS="-Dcom.sun.management.jmxremote"  export JAVA_OPTS="$JAVA_OPTS -Djava.rmi.server.hostname=localhost"    startGrails org.codehaus.groovy.grails.cli.GrailsScriptRunner "
在本期的 精通 Grails 中,作者 Scott Davis 將解釋為何 Grails 已經可以在企業中使用。您將看到如何將 Grails 與企業級庫結合使用,包括 Java™ 管理擴展(Java Management Extensions,JMX)、Spring 和 log4j。

常常有人問我 Grails 是否已經可以在企業中使用。簡單的回答是 “是”。而我通常給出更加詳細的回答:“只要您覺得 Spring 和 Hibernate(Grails 所依賴的底層技術)已經就緒;只要您覺得 Tomcat 或 JBoss(或 Java 企業版[Java EE])應用伺服器已經就緒;只要您覺得 MySQL 或 PostgreSQL(或者您使用的資料庫)已經就緒;只要您覺得 Java 編程已經企業就緒,那麼 Grails 就已經企業就緒” 。

British Sky Broadcasting Group 最近將它的 Web 站點遷移到了 Grails。他們現在每月的點擊量達到 1.1 億次。LinkedIn.com 在其站點的某些商業部分使用 Grails。Tropicana Juice 在英國有一個 Web 站點,該站點幾年來一直在 Grails 上運行。Grails.org 本身就是用 Grails 編寫的,每月支持 70,000 多次下載。而 SpringSource 最近有關 G2One(Groovy 和 Grails 所在的公司)的問卷調查結果完全可以打消 Groovy 和 Grails 是否適合企業使用的任何疑慮。

Groovy 有時候看起來比較奇怪,最重要的是要記住,它完全是用普通的 Java 代碼實現的。儘管 Grails 開發與其他典型的 Java Web 框架看起來很不一樣,但最終您仍然會得到一個與 Java EE 兼容的 WAR 文件。

在這篇文章中,您將探討一些用於監控和配置的企業級工具。學習如何使用 JMX 調整 Grails 應用程序。本文將簡要介紹 Grails 中的 Spring 配置。您還會看到如何在 Config.groovy 中首次指定 log4j 設置,以及如何使用 JMX 動態調整它們。

關於本系列

Grails 是一種新型 Web 開發框架,它將常見的 Spring 和 Hibernate 等 Java 技術與當前流行的配置優於約定等實踐相結合。Grails 是用 Groovy 編寫的,它可以與遺留 Java 代碼無縫集成,同時又加入了腳本編程語言的靈活性和動態性。學習完 Grails 之後,您將徹底改變看待 Web 開發的方式。

實現 JMX 工具

JMX 是 2000 年推出的。更確切地說,它是最古老的 JSR 之一 — JSR 3。隨著 Java 語言在伺服器上越來越流行,遠程優化和配置實時運行應用程序成為平台的關鍵部分。在 2004 年,Sun 使用 JMX 實現了 JVM 並推出了支持工具,比如針對 Java 1.5 JDK 的 JConsole。

JMX 通過一個統一的介面提供 JVM 內省機制、應用伺服器和類。這些不同的組件通過受管 bean(簡寫為 MBean)呈現給管理控制台。

MBeans 就像汽車儀錶板上的各種儀錶、刻度盤和開關。有些儀器是只讀的,就像速度計一樣;有些儀器是 “可寫的”,就像加速器一樣。但 MBean 是遠程管理工具,所以這個儀錶板比喻不是很不恰當。可以將其想象為遠程打開汽車的轉向燈或改變車裡的電台頻道。

啟用本地 JMX 代理

本地還是遠程?

對開發和測試而言,在本地同時運行 JMX 代理和客戶機通常是最簡單的事情。但在實際生產環境中遠程監控代理時,JMX 的好處就會凸顯出來。JConsole 與其他任何 Java 進程一樣佔用系統資源(RAM、CPU 周期等)。這會出現問題,特別是監控的生產伺服器的負載壓力較大時。但更重要的是,能夠從一個地方監控多台伺服器將使您成為數字領域的佼佼者。

當然,遠程監控生產伺服器還可以恰當保護它們的安全。您可以設置密碼保護或使用更好的公/私鑰身份驗證(請參閱 參考資料)。

要使用 JMX 進行監控,則必須先啟用它。在 Java 5 中,您必須在運行時為 JVM 提供幾個與 JMX 相關的標誌(在 Java 6 中,這些設置已經就緒,不過您一定要自己設置的話,也是可以的)。在 JMX 中,要設置一個 JMX 代理。清單 1 顯示了 JVM 參數:


清單 1. 啟用 JMX 監控的 JVM 參數
				  -Dcom.sun.management.jmxremote   -Djava.rmi.server.hostname=localhost  

一些教程建議創建一個全局 JAVA_OPTS 環境變數來保存 JMX 標誌。其他教程則建議在命令行輸入標誌:java -Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=localhost someExampleClass。

兩種方法都是可行的,但是對生產環境而言它們都不是最好的。我發現最好的方法是在伺服器的啟動腳本中設置這些值。如果每次重新啟動伺服器時都要輸入這些複雜的標誌,則表明這是一個不好的解決方案。應避免設置 CLASSPATH 和 JAVA_OPTS 等全局變數,原因有兩個:在複製伺服器(在伺服器之間複製一個一致的啟動腳本更容易)時增加了不必要的配置步驟,而且它們強制同一機器上的所有 Java 進程共享同一配置。是的,您可以創建一個詳細的清單來提醒您這些瑣碎的配置細節,但是記錄複雜的東西遠不如將複雜去掉有效。

對於 UNIX®、Linux® 和 Mac OS X 系統,Grails 啟動腳本是 $GRAILS_HOME/bin/grails。編輯這個文件,添加兩個 JAVA_OPTS 行,如清單 2 所示:


清單 2. 在 Grails 啟動腳本中為 UNIX、Linux 和 Mac OS X 啟用 JMX 監控
___FCKpd___1

對於 Windows®,Grails 啟動腳本是 $GRAILS_HOME/bin/grails.bat。在調用 startGrails.bat 之前,向 grails.bat 添加兩行,如清單 3 所示:


清單 3. 在 Grails 中為 Windows 啟用 JMX 監控
				  set JAVA_OPTS=-Dcom.sun.management.jmxremote  set JAVA_OPTS=%JAVA_OPTS% -Djava.rmi.server.hostname=localhost  

在兩個腳本中,注意第一個 JAVA_OPTS 變數賦值覆蓋了全局環境變數(如果有的話)。這個設置只覆蓋著一個進程 — 它不會對整個系統的全局變數進行賦值。我這樣做的目的是防止全局設置影響本地設置。如果您依賴於已經正確設置的全局值,請確保在開始賦值時包含現有變數,像我在清單 2 和清單 3 的第二行中那樣。

現在,輸入 grails run-app 啟動 Grails。您看到的內容與控制台輸出中的完全相同,不過應用伺服器現在已經可以進行監控。

使用一個 JMX 客戶機來監控 JMX 代理。這是一個類似 JConsole 的桌面 GUI(包含在 Java 5 及更高版本中)或 Web UI(包含在大多數伺服器中,比如 Tomcat 和 JBoss)。甚至可以編寫代碼來監控代理,在本文快結束時將提到。

打開第二個命令行窗口,輸入 jconsole。您將在本地 JML 代理列表中看到 Grails,如圖 1 所示。單擊 Grails,然後單擊 Connect 按鈕。


圖 1. JConsole 列出了本地 JMX 代理

出於安全考慮,只能在使用 NTFS 的 Windows 系統上訪問本地 JMX。如果系統使用的是 FAT 或 FAT32,可能會出現問題。但不要擔心。在接下來的部分中,我將說明如何設置 JMX 代理進行遠程訪問。就算代理和客戶機剛好位於同一機器上,也不會遇到本地安全問題。

連接之後,您應該看到類似圖 2 所示的摘要頁面:


圖 2. JConsole 摘要頁面

單擊 Memory、Threads、Classes 和 VM 選項卡。您可以實時查看 JVM 的內部情況。如果伺服器是在物理內存上運行,那麼您可以看到實時線程數,甚至能夠看到伺服器的已經運行時間。這些選項卡非常有趣,不過您馬上要將注意力轉向 MBeans 選項卡 — 這裡將會出現您需要的類。

啟用遠程 JMX 代理

不要在工作時嘗試這個操作

永遠不要在生產中使用這個配置。出於演示目的,我關閉了所有身份驗證和加密。要了解如何保護 JMX 代理的遠程管理,請參閱 參考資料。

要設置 JMX 代理以接受遠程連接,需要向 JVM 傳遞另外幾個與 JMX 相關的標誌。這幾個標誌打開一個管理埠並配置安全設置(或本例中的 lack thereof)。

向 Grails 啟動腳本添加三個新行,如清單 4 所示:


清單 4. 在 Grails 啟動腳本中啟用遠程 JMX 監控
				  export JAVA_OPTS="-Dcom.sun.management.jmxremote"      export JAVA_OPTS=" $JAVA_OPTS -Djava.rmi.server.hostname=localhost"  export JAVA_OPTS=" $JAVA_OPTS -Dcom.sun.management.jmxremote.port=9004"  export JAVA_OPTS=" $JAVA_OPTS -Dcom.sun.management.jmxremote.authenticate=false"  export JAVA_OPTS=" $JAVA_OPTS -Dcom.sun.management.jmxremote.ssl=false"  

使用這些設置重新啟動 Grails。還要重新啟動 JConsole。這次,單擊 Remote 選項卡並連接到埠 9004 上的 localhost,如圖 3 所示:


圖 3. 在 JConsole 中連接到遠程 JMX 代理

這是一種驗證是否連接上遠程 JVM(即使它在同一系統上運行)的快速方法。單擊 MBeans 選項卡。展開左邊的 java.lang 樹。單擊 Runtime 元素。然後在屏幕右側的屬性窗口中雙擊 InputArguments。您應該看到所有遠程 JMX 設置,如圖 4 所示:


圖 4. 傳遞給 JVM 的 JMX 遠程代理標誌

讓這個窗口保持打開。單擊 Connection 菜單打開一個新的連接。單擊 Remote 選項卡,這次接受默認值(埠 0 上的 localhost)。展開 Runtime MBean 的 InputArguments。注意這裡沒有遠程 JMX 標誌(如圖 5 所示):


圖 5. 監控兩個不同的 JMX 代理

如果標題欄(監控本身)的提示不夠清楚,注意您剛打開的第二個 JConsole 窗口,它監控 JConsole 應用程序本身。

現在您啟動了 JConsole 並監控 Grails 應用程序,此時應該使用它進行一些實際操作了,比如調整登錄設置,不過在進行該操作之前,必須先理解最後一個 JMX 難點:MBean 伺服器。





MBean 伺服器、Grails 和 Spring

您在 JConsole 上單擊的 Runtime 元素是一個 MBean。為了讓 MBean 呈現給 JMX 客戶機,必須使用一個內部運行有 JMX 代理的 MBean 伺服器註冊它。有些人將術語 “JMX 代理” 等同於 “MBean 伺服器”,但從技術上講,MBean 伺服器是在 JMX 代理內部運行的眾多組件中的一個。

要以編程方式註冊 MBean,需調用 MBeanServer.registerMBean()。不過,在 Grails 中,更準確地說,這是由一個配置文件(一個 Spring 配置文件)管理的。

Spring 是 Grails 的核心。它是控制所有類如何交互的依賴項注入框架(有關 Spring 的更多信息,請參閱 參考資料)。

從 JMX 角度,您可能會想:我在用 MBean 伺服器註冊這個 MBean。但從 Spring 角度,您應該這樣考慮:我在將 MBean 注入到 MBean 伺服器中。動作對象可能不同,但最終結果是一樣的:MBean 變為對 JMX 客戶機是可視的。

首先在 grails-app/conf/spring 中創建一個名為 resources.xml 的文件(在本文後面,您將明白 resources.groovy 和 resources.xml 的關係)。設置 resources.xml,如清單 5 所示:


清單 5. 在 resources.xml 中設置 Spring/JMX 基礎設施
				  <beans xmlns="http://www.springframework.org/schema/beans"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://www.springframework.org/schema/beans             http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">      <bean id="mbeanServer"           class="org.springframework.jmx.support.MBeanServerFactoryBean">     	<property name="locateExistingServerIfPossible" value="true" />    </bean>        <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">	      <property name="server" ref="mbeanServer"/>      <property name="beans">        <map>        </map>      </property>    </bean>     </beans>  

如果想確保基本配置是正確的,那麼現在可重新啟動 Grails,但只解決問題的一半:您有了一台 MBean 伺服器,但是沒有任何 MBean。此時看到的兩個 bean(mbeanServer 和 exporter)是需要註冊 MBean 的基礎設施。mbeanServer bean 保存一個到現有 MBean 伺服器的引用。mbeanServer bean 被注入到 exporter bean — 將 MBean 列表呈現給 JMX 客戶機(比如 JConsole)的類。現在僅需將 MBean 添加到 exporter bean 內部的 bean 映射中,以註冊它。下一小節將進行此操作。





通過 Grails 使用 log4j

打開 grails-app/conf/Config.groovy 查看 log4j 設置(如清單 6 所示):


清單 6. Config.groovy 中的 log4j 設置
				  log4j {      appender.stdout = "org.apache.log4j.ConsoleAppender"      appender.'stdout.layout'="org.apache.log4j.PatternLayout"      appender.'stdout.layout.ConversionPattern'='[%r] %c{2} %m%n'      // and so on...  }  

啟動 Grails 應用程序時,命令提示符上出現的大多數消息是 log4j 消息。這要歸功於 org.apache.log4j.ConsoleAppender(更多關於 log4j 的基礎知識,請參閱 參考資料)。

註冊 log4j MBean

如果需要在沒有 JMX 的情況下調整 Grails 的登錄設置,只需簡單地編輯這個文件並重新啟動伺服器,但如果更願意調整這些設置而不重新啟動伺服器,或者想遠程調整它們,那應該怎樣做呢?這看起來似乎是 JMX 可選的完美方法。幸運的是,log4j 附帶一個方便執行這些任務 MBean。您所需做的只是註冊 log4j MBean。

將 entry 的 XML(如清單 7 所示)添加到 resources.xml。這將把 log4j MBean 注入到 MBean 伺服器。


清單 7. 將 MBean 注入到 MBean 伺服器
				  <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">	    <property name="server" ref="mbeanServer"/>    <property name="beans">      <map>        <entry key="log4j:hierarchy=default">          <bean class="org.apache.log4j.jmx.HierarchyDynamicMBean"/>        </entry>              </map>    </property>  </bean>  

重新啟動 Grails,然後重新啟動 JConsole。如果連接到埠 9004 上的 localhost,新的 log4j MBean 應該顯示在 MBeans 選項卡中。展開 log4j 樹元素,單擊默認值,然後單擊 Info 選項卡。從剛添加到 resources.xml(參見圖 5)的條目中,可以看到配置片段:


圖 6. 查看默認 MBean 信息

現在可以通過 JMX 看到 log4j 了,下一步是調整一些登錄設置。

動態更改 log4j 設置

假設現在 Grails 應用程序表現異常。您應該查找問題的根源。查看 grails-app/conf/Config.groovy,您會發現根登錄程序將它的輸出發送到控制台,但過濾器被設置為 error — rootLogger="error,stdout"。您希望將登錄級別更改為 trace 來提高控制台的輸出量。

看一下 JConsole。在 log4j 文件夾下,您應該可以看到根 MBean。可以看到優先順序屬性被設置為 ERROR,就像在 Config.groovy 中一樣。雙擊 ERROR 值並輸入 TRACE,如圖 6 所示:


圖 7. 將根登錄程序優先順序從 ERROR 更改為 TRACE

為了驗證控制台比以前更好用,在瀏覽器中,在 Grails 應用程序的主頁上單擊到 AirportMappingController 的鏈接。在大量新的輸出中,您應該可以找到一些有關 Grails 如何導入初始列表的詳細信息。請參閱清單 8 中的樣例:


清單 8. 增加 log4j 輸出
				  [11277653] metaclass.RedirectDynamicMethod     Dynamic method [redirect] looking up URL mapping for     controller [airportMapping] and action [list] and     params [["action":"index", "controller":"airportMapping"]]     with [URL Mappings  ------------  org.codehaus.groovy.grails.web.mapping.ResponseCodeUrlMapping@1bab0b  /rest/airport/(*)?  /(*)/(*)?/(*)?  ]  [11277653] metaclass.RedirectDynamicMethod Dynamic method     [redirect] mapped to URL [/trip/airportMapping/list]  [11277653] metaclass.RedirectDynamicMethod Dynamic method     [redirect] forwarding request to [/trip/airportMapping/list]  [11277653] metaclass.RedirectDynamicMethod Executing redirect     with response     [com.opensymphony.module.sitemesh.filter.PageResponseWrapper@19243f]  

什麼時候可以安全忽略 Fatal Error?

如果您曾經運行過 Grails 1.0.3,可能會注意到一條頻繁出現在控制台輸出中的奇怪錯誤 — [Fatal Error] :-1:-1: Premature end of file。大多數人僅僅是忽略它,因為它似乎真的不會引起任何錯誤(不管致命與否)。

如果將登錄級別轉變為 trace,您就會看到有關致命錯誤的詳細信息:converters.XMLParsingParameterCreationListener Error parsing incoming XML request: Error parsing XML。

正如大量冗長的日誌輸出所解釋的那樣,Grails 試圖解析每個傳入請求,把請求當作 XML。大多數請求不是 XML,因此請求處理程序將根據實際情況報告錯誤,但仍然會正確地處理請求。

這個 “謊報軍情的小 bug” 在版本 1.0.4 中得到了修復。

更改 log4j ConversionPattern

現在需要更改輸出模式。在 Config.groovy 中,使用下面這一行設置模式:appender.'stdout.layout.ConversionPattern'='[%r] %c{2} %m%n'。查看 log4j 文檔,您決定將它設置為更具描述性的東西。

單擊 JConsole 中的 stdout MBean。將 conversionPattern 屬性從它的原始值更改為 [%5p] %d{hh:mm:ss} (%F:%M:%L)%n%m%n%n。生成一些新的日誌輸出后,我將描述這些奇怪的符號的含義(請參閱 參考資料,了解設置 conversionPattern 的更多信息)。


圖 8. 在 PatternLayout 中更改 conversionPattern

現在再次在 Web 瀏覽器中單擊主頁鏈接和 AirportMappingController 鏈接。輸出的格式發生了很大變化,如清單 9 所示:


清單 9. 使用新的 conversionPattern 的控制台輸出
				  [DEBUG] 09:04:47 (RedirectDynamicMethod.java:invoke:127)  Dynamic method [redirect] looking up URL mapping for controller   [airportMapping] and action [list] and params   [["action":"index", "controller":"airportMapping"]] with [URL Mappings  ------------  org.codehaus.groovy.grails.web.mapping.ResponseCodeUrlMapping@e73cb7  /rest/airport/(*)?  /(*)/(*)?/(*)?  ]    [DEBUG] 09:04:47 (RedirectDynamicMethod.java:invoke:144)  Dynamic method [redirect] mapped to URL [/trip/airportMapping/list]    [DEBUG] 09:04:47 (RedirectDynamicMethod.java:redirectResponse:162)  Dynamic method [redirect] forwarding request to [/trip/airportMapping/list]    [DEBUG] 09:04:47 (RedirectDynamicMethod.java:redirectResponse:168)  Executing redirect with response      [com.opensymphony.module.sitemesh.filter.PageResponseWrapper@47b2e7]  

現在您可以看到輸出,以下是詳細過程:%p 寫出優先順序別。這些消息很明顯是 DEBUG 級別。%d{hh:mm:ss} 以小時:分鐘:秒的格式顯示日期戳。(%F:%M:%L) 將文件名、方法和行編號放在括弧內。最後,%n%m%n%n 寫入一個新行、消息和其他兩行。

通過 JMX 對 log4j 所做的更改不是持久化的。如果重新啟動 Grails,它會恢復到 Config.groovy 中的持久化設置。這意味著您可以任意處理 JMX 設置,而不用擔心會永久打亂事情。對於 ConversionPattern,使用 JMX 是體驗設置的很好方法,您可以找到最喜歡的設置。但是不要忘了將模式複製到 Config.groovy,以使更改是持久化的。





查看 Hibernate DEBUG 輸出

回到先前的假設,您正在調試一個實時 Grails 應用程序,但還沒有找到您需要的東西。將根 MBean 的優先順序屬性設置回 ERROR 來減少干擾。

可能問題就出在 Hibernate。再回過頭看看 Config.groovy,您會發現 org.hibernate 包的登錄輸出被設置為 off。不要改變整個應用程序的輸出級別,而是集中於特定的包,這樣可能會獲得更多的信息。

在 JConsole 中,單擊默認 MBean。除了更改屬性值以外,您還可以調用 MBean 上的方法。單擊 Operations 選項卡。為名稱參數輸入 org.hibernate 並單擊 addLoggerMBean 按鈕。您應該會看到一個新的 MBean 出現在左邊的樹中。

單擊新的 org.hibernate MBean 並將優先順序屬性更改為 DEBUG,如圖 9 所示:


圖 9. 更改 org.hibernate MBean 上的優先順序

現在返回到 Web 瀏覽器,單擊主鏈接,並再次單擊 AirportMappingController 。應該會看到一大串 DEBUG 日誌語句,如清單 10 所示:


清單 10. Hibernate log4j 輸出
				  [DEBUG] 10:05:52 (AbstractBatcher.java:logOpenPreparedStatement:366)  about to open PreparedStatement (open PreparedStatements: 0, globally: 0)    [DEBUG] 10:05:52 (ConnectionManager.java:openConnection:421)  opening JDBC connection    [DEBUG] 10:05:52 (AbstractBatcher.java:log:401)  select this_.airport_id as airport1_0_0_, this_.locid as locid0_0_,   this_.latitude as latitude0_0_, this_.longitude as longitude0_0_,   this_.airport_name as airport5_0_0_, this_.state as state0_0_   from usgs_airports this_ limit ?    [DEBUG] 10:05:52 (AbstractBatcher.java:logOpenResults:382)  about to open ResultSet (open ResultSets: 0, globally: 0)    [DEBUG] 10:05:52 (Loader.java:getRow:1173)  result row: EntityKey[AirportMapping#1]    [DEBUG] 10:05:52 (Loader.java:getRow:1173)  result row: EntityKey[AirportMapping#2]    

花一點時間查看 Hibernate DEBUG 輸出。您詳細了解到何時從資料庫挑選數據,並轉換為一個由 bean 組成的 ArrayList。





使用 Spring Bean Builder

現在您已經知道了如何通過 resources.xml 配置 JMX,因此可以進行新的實踐了。Grails 通過一個替代文件 resources.groovy 支持 Spring 配置。將 grails-app/conf/spring/resources.xml 重命名為 resources.xml.old。將如清單 11 所示的代碼添加到 resources.groovy 中:


清單 11. 使用 Bean Builder 配置 Spring
				  import org.springframework.jmx.support.MBeanServerFactoryBean  import org.springframework.jmx.export.MBeanExporter  import org.apache.log4j.jmx.HierarchyDynamicMBean    beans = {    log4jBean(HierarchyDynamicMBean)        mbeanServer(MBeanServerFactoryBean) {      locateExistingServerIfPossible=true    }        exporter(MBeanExporter) {      server = mbeanServer    	beans = ["log4j:hierarchy=default":log4jBean]    }      }  

如您所見,Spring bean 使用 Groovy 代碼(而不是 XML)配置的。您已經在 “Grails 與遺留資料庫” 和 “RESTful Grails” 中看到現實中的 Groovy MarkupBuilder。主題有點變化 — 一個專門為 Spring 配置定義 bean 的 Bean Builder。

重新啟動 Grails 和 JConsole。確認 XML 配置中沒有任何更改。

使用 XML 來配置 Spring 可以輕鬆運用 Web 各種優勢 — 可以從大量源複製粘貼代碼片段。但是使用 Bean Builder 更符合 Grail 中的其餘配置。使用 Grails 到現在,您已經看到了 DataSource.groovy、Config.groovy、BootStrap.groovy 和 Events.groovy,這只是其中一小部分。在代碼中進行配置,這意味著您可以執行一些操作,比如基於運行的環境有條件地呈現 MBean。

例如,清單 12 顯示了如何在生產環境中呈現 log4jBean,但在開發環境中隱藏它:


清單 12. 有條件地呈現 JMX bean
				  import org.springframework.jmx.support.MBeanServerFactoryBean  import org.springframework.jmx.export.MBeanExporter  import org.apache.log4j.jmx.HierarchyDynamicMBean  import grails.util.GrailsUtil    beans = {    log4jBean(HierarchyDynamicMBean)        mbeanServer(MBeanServerFactoryBean) {      locateExistingServerIfPossible=true    }        switch(GrailsUtil.environment){      case "development":      break            case "production":        exporter(MBeanExporter) {          server = mbeanServer        	beans = ["log4j:hierarchy=default":log4jBean]        }              break    }  }  

輸入 grails run-app 並在 JConsole 中確定 log4j MBean 沒有出現在開發模式中。現在輸入 grails prod run-app(或 grails war 並將 WAR 文件部署到您選擇的應用伺服器)。這個 MBean 會一直等待您重新啟動 JConsole。





Groovy 中的 JMX

我最後要向您展示的是如何以編程方式調試 JMX MBean。JConsole GUI 非常漂亮,能夠從 Groovy 腳本進行更改更是增加了它的魅力。

開始之前,創建一個名為 testJmx.groovy 的文件,將清單 13 中的代碼添加到該文件中:


清單 13. 在 Groovy 中調用一個遠程 JMX 代理
				  import javax.management.MBeanServerConnection  import javax.management.remote.JMXConnectorFactory  import javax.management.remote.JMXServiceURL    def agentUrl = "service:jmx:rmi:///jndi/rmi://localhost:9004/jmxrmi"  def connector = JMXConnectorFactory.connect(new JMXServiceURL(agentUrl))  def server = connector.mBeanServerConnection     println "Number of registered MBeans: ${server.mBeanCount}"    println "\nRegistered Domains:"  server.domains.each{println it}    println "\nRegistered MBeans:"  server.queryNames(null, null).each{println it}  

如果 Grails 正在運行,應該可以看到如清單 14 所示的輸出:


清單 14. testJmx.groovy 腳本的輸出
				  $ groovy testJmx.groovy   Number of registered MBeans: 20    Registered Domains:  java.util.logging  JMImplementation  java.lang  log4j    Registered MBeans:  java.lang:type=MemoryManager,name=CodeCacheManager  java.lang:type=Compilation  java.lang:type=GarbageCollector,name=Copy  java.lang:type=MemoryPool,name=Eden Space  log4j:appender=stdout  java.lang:type=Runtime  log4j:hierarchy=default  log4j:logger=root  log4j:appender=stdout,layout=org.apache.log4j.PatternLayout  java.lang:type=ClassLoading  java.lang:type=MemoryPool,name=Survivor Space  java.lang:type=Threading  java.lang:type=GarbageCollector,name=MarkSweepCompact  java.util.logging:type=Logging  java.lang:type=Memory  java.lang:type=OperatingSystem  java.lang:type=MemoryPool,name=Code Cache  java.lang:type=MemoryPool,name=Tenured Gen  java.lang:type=MemoryPool,name=Perm Gen  JMImplementation:type=MBeanServerDelegate  

警告

testJmx.groovy 腳本可能會拋出一條類似清單 15 所示的 groovy.lang.MissingMethodException:


清單 15. 可能拋出的 JMX 異常
					  Caught: groovy.lang.MissingMethodException: No signature of method:    javax.management.remote.rmi.RMIConnector$RemoteMBeanServerConnection.queryNames()    is applicable for argument types: (java.lang.String, null)   

如果發生這種情況,請從 $GROOVY_HOME/lib 中刪除 mx4j-3.0.2.jar。它包含在 Groovy 發布版中,以通過 JMX 支持 1.4 JDK,但它與 Java 平台的更高版本衝突。

這個腳本中有趣的部分來自 javax.management.MBeanServer,javax.management.MBeanServer 是調用 connector.mBeanServerConnection 時返回的(記住,Java 中的 getFoo() 方法調用在 Groovy 中可以簡寫為 foo)。調用 server.mBeanCount 返回已註冊 MBean 的數量。調用 server.domains 返回一個由域名組成的 String[]。域名是 MBean 標識符的第一部分 — 用逗號分隔的名/值對列表完全限定名稱。調用 server.queryNames(null, null) 返回一個由所有已註冊 MBean 組成的 Set(要更多地了解 MBeanServer 類的可用方法,請參閱 參考資料)。

為了獲得某個特定 MBean,請將清單 16 中的代碼添加到腳本底部:


清單 16. 獲得一個 MBean
				  println "\nHere is the Runtime MBean:"  def mbean = new GroovyMBean(server, "java.lang:type=Runtime")  println mbean  

有了一個到 MBean 伺服器的連接並知道 MBean 的名稱后,使用一行即可獲取一個新的 GroovyMBean。清單 17 顯示了腳本輸出:


清單 17. GroovyMBean 輸出
				  Here is the Runtime MBean:  MBean Name:    java.lang:type=Runtime      Attributes:    (r) javax.management.openmbean.TabularData SystemProperties    (r) java.lang.String VmVersion    (r) java.lang.String VmName    (r) java.lang.String SpecName    (r) [Ljava.lang.String; InputArguments    (r) java.lang.String ManagementSpecVersion    (r) java.lang.String SpecVendor    (r) long Uptime    (r) long StartTime    (r) java.lang.String LibraryPath    (r) java.lang.String BootClassPath    (r) java.lang.String VmVendor    (r) java.lang.String ClassPath    (r) java.lang.String SpecVersion    (r) java.lang.String Name    (r) boolean BootClassPathSupported  

是否還記得本文前面提到的 InputArguments?它們是傳遞給 JVM 的所有 -D 參數。使用這些參數來確認您確實連接到了遠程 JMX 代理。再添加兩行代碼(如清單 18 所示)以輸出 String[]:


清單 18. 從運行時 MBean 獲取 InputArguments
				  println "\nHere are the InputArguments:"  mbean.InputArguments.each{println it}  

如果看到清單 19 所示的輸出,就說明您圓滿完成了所有任務:


清單 19. 顯示 InputArguments
				  Here are the InputArguments:  -Xserver  -Xmx512M  -Dcom.sun.management.jmxremote  -Djava.rmi.server.hostname=localhost  -Dcom.sun.management.jmxremote.port=9004  -Dcom.sun.management.jmxremote.authenticate=false  -Dcom.sun.management.jmxremote.ssl=false  -Dprogram.name=grails  -Dgroovy.starter.conf=/opt/grails/conf/groovy-starter.conf  -Dgrails.home=/opt/grails  -Dbase.dir=.  -Dtools.jar=/Library/Java/Home/lib/tools.jar  

有關 GroovyMBean 的更多信息,請參閱 參考資料。





結束語

Grails 是已經可以在企業中使用。常用企業庫,比如 JMX、Spring 和 log4j 都可以在 Grails 中使用,因為雖然表面不像,但您仍然是進行傳統的 Java EE 開發。

本文是 Trip Planner 應用程序專欄今年的最後一篇文章。我希望保持這系列文章的主題的一致性,以重點講解核心 Grails 功能。在明年的專欄中,我將繼續保留這種風格,但同時我還想納入各種 Grails 應用程序,以擴大視野。

例如,下個月我將介紹一種新的博客系統。您學習關於如何啟動新的 Grails 應用程序的新技術,但它絕不是 “新瓶裝舊酒”。您將重溫 Grails 的 RESTful,但是是在設置完全 Atom 基礎設施的環境中。您將再次使用 JSON 和 Ajax,??過這次是啟用日程表和標誌雲。幾個月之後,我將使用另一種新思維。

Grails 繼續獲得了每個新 Web 站點的大力支持。成熟 Web 框架的標誌是能夠以多種方式使用它。明年的精通 Grails 文章將演示 Grails 中可能實現的各種 Web 站點。到那時,您將享受到精通 Grails 帶來的樂趣。 (責任編輯:A6)

cnnew1_cnnew1@"

對於 Windows®,Grails 啟動腳本是 $GRAILS_HOME/bin/grails.bat。在調用 startGrails.bat 之前,向 grails.bat 添加兩行,如清單 3 所示:


清單 3. 在 Grails 中為 Windows 啟用 JMX 監控
___FCKpd___2

在兩個腳本中,注意第一個 JAVA_OPTS 變數賦值覆蓋了全局環境變數(如果有的話)。這個設置只覆蓋著一個進程 — 它不會對整個系統的全局變數進行賦值。我這樣做的目的是防止全局設置影響本地設置。如果您依賴於已經正確設置的全局值,請確保在開始賦值時包含現有變數,像我在清單 2 和清單 3 的第二行中那樣。

現在,輸入 grails run-app 啟動 Grails。您看到的內容與控制台輸出中的完全相同,不過應用伺服器現在已經可以進行監控。

使用一個 JMX 客戶機來監控 JMX 代理。這是一個類似 JConsole 的桌面 GUI(包含在 Java 5 及更高版本中)或 Web UI(包含在大多數伺服器中,比如 Tomcat 和 JBoss)。甚至可以編寫代碼來監控代理,在本文快結束時將提到。

打開第二個命令行窗口,輸入 jconsole。您將在本地 JML 代理列表中看到 Grails,如圖 1 所示。單擊 Grails,然後單擊 Connect 按鈕。


圖 1. JConsole 列出了本地 JMX 代理

出於安全考慮,只能在使用 NTFS 的 Windows 系統上訪問本地 JMX。如果系統使用的是 FAT 或 FAT32,可能會出現問題。但不要擔心。在接下來的部分中,我將說明如何設置 JMX 代理進行遠程訪問。就算代理和客戶機剛好位於同一機器上,也不會遇到本地安全問題。

連接之後,您應該看到類似圖 2 所示的摘要頁面:


圖 2. JConsole 摘要頁面

單擊 Memory、Threads、Classes 和 VM 選項卡。您可以實時查看 JVM 的內部情況。如果伺服器是在物理內存上運行,那麼您可以看到實時線程數,甚至能夠看到伺服器的已經運行時間。這些選項卡非常有趣,不過您馬上要將注意力轉向 MBeans 選項卡 — 這裡將會出現您需要的類。

啟用遠程 JMX 代理

不要在工作時嘗試這個操作

永遠不要在生產中使用這個配置。出於演示目的,我關閉了所有身份驗證和加密。要了解如何保護 JMX 代理的遠程管理,請參閱 參考資料。

要設置 JMX 代理以接受遠程連接,需要向 JVM 傳遞另外幾個與 JMX 相關的標誌。這幾個標誌打開一個管理埠並配置安全設置(或本例中的 lack thereof)。

向 Grails 啟動腳本添加三個新行,如清單 4 所示:


清單 4. 在 Grails 啟動腳本中啟用遠程 JMX 監控
___FCKpd___3

使用這些設置重新啟動 Grails。還要重新啟動 JConsole。這次,單擊 Remote 選項卡並連接到埠 9004 上的 localhost,如圖 3 所示:


圖 3. 在 JConsole 中連接到遠程 JMX 代理

這是一種驗證是否連接上遠程 JVM(即使它在同一系統上運行)的快速方法。單擊 MBeans 選項卡。展開左邊的 java.lang 樹。單擊 Runtime 元素。然後在屏幕右側的屬性窗口中雙擊 InputArguments。您應該看到所有遠程 JMX 設置,如圖 4 所示:


圖 4. 傳遞給 JVM 的 JMX 遠程代理標誌

讓這個窗口保持打開。單擊 Connection 菜單打開一個新的連接。單擊 Remote 選項卡,這次接受默認值(埠 0 上的 localhost)。展開 Runtime MBean 的 InputArguments。注意這裡沒有遠程 JMX 標誌(如圖 5 所示):


圖 5. 監控兩個不同的 JMX 代理

如果標題欄(監控本身)的提示不夠清楚,注意您剛打開的第二個 JConsole 窗口,它監控 JConsole 應用程序本身。

現在您啟動了 JConsole 並監控 Grails 應用程序,此時應該使用它進行一些實際操作了,比如調整登錄設置,不過在進行該操作之前,必須先理解最後一個 JMX 難點:MBean 伺服器。





MBean 伺服器、Grails 和 Spring

您在 JConsole 上單擊的 Runtime 元素是一個 MBean。為了讓 MBean 呈現給 JMX 客戶機,必須使用一個內部運行有 JMX 代理的 MBean 伺服器註冊它。有些人將術語 “JMX 代理” 等同於 “MBean 伺服器”,但從技術上講,MBean 伺服器是在 JMX 代理內部運行的眾多組件中的一個。

要以編程方式註冊 MBean,需調用 MBeanServer.registerMBean()。不過,在 Grails 中,更準確地說,這是由一個配置文件(一個 Spring 配置文件)管理的。

Spring 是 Grails 的核心。它是控制所有類如何交互的依賴項注入框架(有關 Spring 的更多信息,請參閱 參考資料)。

從 JMX 角度,您可能會想:我在用 MBean 伺服器註冊這個 MBean。但從 Spring 角度,您應該這樣考慮:我在將 MBean 注入到 MBean 伺服器中。動作對象可能不同,但最終結果是一樣的:MBean 變為對 JMX 客戶機是可視的。

首先在 grails-app/conf/spring 中創建一個名為 resources.xml 的文件(在本文後面,您將明白 resources.groovy 和 resources.xml 的關係)。設置 resources.xml,如清單 5 所示:


清單 5. 在 resources.xml 中設置 Spring/JMX 基礎設施
___FCKpd___4

如果想確保基本配置是正確的,那麼現在可重新啟動 Grails,但只解決問題的一半:您有了一台 MBean 伺服器,但是沒有任何 MBean。此時看到的兩個 bean(mbeanServer 和 exporter)是需要註冊 MBean 的基礎設施。mbeanServer bean 保存一個到現有 MBean 伺服器的引用。mbeanServer bean 被注入到 exporter bean — 將 MBean 列表呈現給 JMX 客戶機(比如 JConsole)的類。現在僅需將 MBean 添加到 exporter bean 內部的 bean 映射中,以註冊它。下一小節將進行此操作。





通過 Grails 使用 log4j

打開 grails-app/conf/Config.groovy 查看 log4j 設置(如清單 6 所示):


清單 6. Config.groovy 中的 log4j 設置
___FCKpd___5

啟動 Grails 應用程序時,命令提示符上出現的大多數消息是 log4j 消息。這要歸功於 org.apache.log4j.ConsoleAppender(更多關於 log4j 的基礎知識,請參閱 參考資料)。

註冊 log4j MBean

如果需要在沒有 JMX 的情況下調整 Grails 的登錄設置,只需簡單地編輯這個文件並重新啟動伺服器,但如果更願意調整這些設置而不重新啟動伺服器,或者想遠程調整它們,那應該怎樣做呢?這看起來似乎是 JMX 可選的完美方法。幸運的是,log4j 附帶一個方便執行這些任務 MBean。您所需做的只是註冊 log4j MBean。

將 entry 的 XML(如清單 7 所示)添加到 resources.xml。這將把 log4j MBean 注入到 MBean 伺服器。


清單 7. 將 MBean 注入到 MBean 伺服器
___FCKpd___6

重新啟動 Grails,然後重新啟動 JConsole。如果連接到埠 9004 上的 localhost,新的 log4j MBean 應該顯示在 MBeans 選項卡中。展開 log4j 樹元素,單擊默認值,然後單擊 Info 選項卡。從剛添加到 resources.xml(參見圖 5)的條目中,可以看到配置片段:


圖 6. 查看默認 MBean 信息

現在可以通過 JMX 看到 log4j 了,下一步是調整一些登錄設置。

動態更改 log4j 設置

假設現在 Grails 應用程序表現異常。您應該查找問題的根源。查看 grails-app/conf/Config.groovy,您會發現根登錄程序將它的輸出發送到控制台,但過濾器被設置為 error — rootLogger="error,stdout"。您希望將登錄級別更改為 trace 來提高控制台的輸出量。

看一下 JConsole。在 log4j 文件夾下,您應該可以看到根 MBean。可以看到優先順序屬性被設置為 ERROR,就像在 Config.groovy 中一樣。雙擊 ERROR 值並輸入 TRACE,如圖 6 所示:


圖 7. 將根登錄程序優先順序從 ERROR 更改為 TRACE

為了驗證控制台比以前更好用,在瀏覽器中,在 Grails 應用程序的主頁上單擊到 AirportMappingController 的鏈接。在大量新的輸出中,您應該可以找到一些有關 Grails 如何導入初始列表的詳細信息。請參閱清單 8 中的樣例:


清單 8. 增加 log4j 輸出
___FCKpd___7

什麼時候可以安全忽略 Fatal Error?

如果您曾經運行過 Grails 1.0.3,可能會注意到一條頻繁出現在控制台輸出中的奇怪錯誤 — [Fatal Error] :-1:-1: Premature end of file。大多數人僅僅是忽略它,因為它似乎真的不會引起任何錯誤(不管致命與否)。

如果將登錄級別轉變為 trace,您就會看到有關致命錯誤的詳細信息:converters.XMLParsingParameterCreationListener Error parsing incoming XML request: Error parsing XML。

正如大量冗長的日誌輸出所解釋的那樣,Grails 試圖解析每個傳入請求,把請求當作 XML。大多數請求不是 XML,因此請求處理程序將根據實際情況報告錯誤,但仍然會正確地處理請求。

這個 “謊報軍情的小 bug” 在版本 1.0.4 中得到了修復。

更改 log4j ConversionPattern

現在需要更改輸出模式。在 Config.groovy 中,使用下面這一行設置模式:appender.'stdout.layout.ConversionPattern'='[%r] %c{2} %m%n'。查看 log4j 文檔,您決定將它設置為更具描述性的東西。

單擊 JConsole 中的 stdout MBean。將 conversionPattern 屬性從它的原始值更改為 [%5p] %d{hh:mm:ss} (%F:%M:%L)%n%m%n%n。生成一些新的日誌輸出后,我將描述這些奇怪的符號的含義(請參閱 參考資料,了解設置 conversionPattern 的更多信息)。


圖 8. 在 PatternLayout 中更改 conversionPattern

現在再次在 Web 瀏覽器中單擊主頁鏈接和 AirportMappingController 鏈接。輸出的格式發生了很大變化,如清單 9 所示:


清單 9. 使用新的 conversionPattern 的控制台輸出
___FCKpd___8

現在您可以看到輸出,以下是詳細過程:%p 寫出優先順序別。這些消息很明顯是 DEBUG 級別。%d{hh:mm:ss} 以小時:分鐘:秒的格式顯示日期戳。(%F:%M:%L) 將文件名、方法和行編號放在括弧內。最後,%n%m%n%n 寫入一個新行、消息和其他兩行。

通過 JMX 對 log4j 所做的更改不是持久化的。如果重新啟動 Grails,它會恢復到 Config.groovy 中的持久化設置。這意味著您可以任意處理 JMX 設置,而不用擔心會永久打亂事情。對於 ConversionPattern,使用 JMX 是體驗設置的很好方法,您可以找到最喜歡的設置。但是不要忘了將模式複製到 Config.groovy,以使更改是持久化的。





查看 Hibernate DEBUG 輸出

回到先前的假設,您正在調試一個實時 Grails 應用程序,但還沒有找到您需要的東西。將根 MBean 的優先順序屬性設置回 ERROR 來減少干擾。

可能問題就出在 Hibernate。再回過頭看看 Config.groovy,您會發現 org.hibernate 包的登錄輸出被設置為 off。不要改變整個應用程序的輸出級別,而是集中於特定的包,這樣可能會獲得更多的信息。

在 JConsole 中,單擊默認 MBean。除了更改屬性值以外,您還可以調用 MBean 上的方法。單擊 Operations 選項卡。為名稱參數輸入 org.hibernate 並單擊 addLoggerMBean 按鈕。您應該會看到一個新的 MBean 出現在左邊的樹中。

單擊新的 org.hibernate MBean 並將優先順序屬性更改為 DEBUG,如圖 9 所示:


圖 9. 更改 org.hibernate MBean 上的優先順序

現在返回到 Web 瀏覽器,單擊主鏈接,並再次單擊 AirportMappingController 。應該會看到一大串 DEBUG 日誌語句,如清單 10 所示:


清單 10. Hibernate log4j 輸出
___FCKpd___9

花一點時間查看 Hibernate DEBUG 輸出。您詳細了解到何時從資料庫挑選數據,並轉換為一個由 bean 組成的 ArrayList。





使用 Spring Bean Builder

現在您已經知道了如何通過 resources.xml 配置 JMX,因此可以進行新的實踐了。Grails 通過一個替代文件 resources.groovy 支持 Spring 配置。將 grails-app/conf/spring/resources.xml 重命名為 resources.xml.old。將如清單 11 所示的代碼添加到 resources.groovy 中:


清單 11. 使用 Bean Builder 配置 Spring
___FCKpd___10

如您所見,Spring bean 使用 Groovy 代碼(而不是 XML)配置的。您已經在 “Grails 與遺留資料庫” 和 “RESTful Grails” 中看到現實中的 Groovy MarkupBuilder。主題有點變化 — 一個專門為 Spring 配置定義 bean 的 Bean Builder。

重新啟動 Grails 和 JConsole。確認 XML 配置中沒有任何更改。

使用 XML 來配置 Spring 可以輕鬆運用 Web 各種優勢 — 可以從大量源複製粘貼代碼片段。但是使用 Bean Builder 更符合 Grail 中的其餘配置。使用 Grails 到現在,您已經看到了 DataSource.groovy、Config.groovy、BootStrap.groovy 和 Events.groovy,這只是其中一小部分。在代碼中進行配置,這意味著您可以執行一些操作,比如基於運行的環境有條件地呈現 MBean。

例如,清單 12 顯示了如何在生產環境中呈現 log4jBean,但在開發環境中隱藏它:


清單 12. 有條件地呈現 JMX bean
___FCKpd___11

輸入 grails run-app 並在 JConsole 中確定 log4j MBean 沒有出現在開發模式中。現在輸入 grails prod run-app(或 grails war 並將 WAR 文件部署到您選擇的應用伺服器)。這個 MBean 會一直等待您重新啟動 JConsole。





Groovy 中的 JMX

我最後要向您展示的是如何以編程方式調試 JMX MBean。JConsole GUI 非常漂亮,能夠從 Groovy 腳本進行更改更是增加了它的魅力。

開始之前,創建一個名為 testJmx.groovy 的文件,將清單 13 中的代碼添加到該文件中:


清單 13. 在 Groovy 中調用一個遠程 JMX 代理
___FCKpd___12

如果 Grails 正在運行,應該可以看到如清單 14 所示的輸出:


清單 14. testJmx.groovy 腳本的輸出
___FCKpd___13

警告

testJmx.groovy 腳本可能會拋出一條類似清單 15 所示的 groovy.lang.MissingMethodException:


清單 15. 可能拋出的 JMX 異常
___FCKpd___14

如果發生這種情況,請從 $GROOVY_HOME/lib 中刪除 mx4j-3.0.2.jar。它包含在 Groovy 發布版中,以通過 JMX 支持 1.4 JDK,但它與 Java 平台的更高版本衝突。

這個腳本中有趣的部分來自 javax.management.MBeanServer,javax.management.MBeanServer 是調用 connector.mBeanServerConnection 時返回的(記住,Java 中的 getFoo() 方法調用在 Groovy 中可以簡寫為 foo)。調用 server.mBeanCount 返回已註冊 MBean 的數量。調用 server.domains 返回一個由域名組成的 String[]。域名是 MBean 標識符的第一部分 — 用逗號分隔的名/值對列表完全限定名稱。調用 server.queryNames(null, null) 返回一個由所有已註冊 MBean 組成的 Set(要更多地了解 MBeanServer 類的可用方法,請參閱 參考資料)。

為了獲得某個特定 MBean,請將清單 16 中的代碼添加到腳本底部:


清單 16. 獲得一個 MBean
___FCKpd___15

有了一個到 MBean 伺服器的連接並知道 MBean 的名稱后,使用一行即可獲取一個新的 GroovyMBean。清單 17 顯示了腳本輸出:


清單 17. GroovyMBean 輸出
___FCKpd___16

是否還記得本文前面提到的 InputArguments?它們是傳遞給 JVM 的所有 -D 參數。使用這些參數來確認您確實連接到了遠程 JMX 代理。再添加兩行代碼(如清單 18 所示)以輸出 String[]:


清單 18. 從運行時 MBean 獲取 InputArguments
___FCKpd___17

如果看到清單 19 所示的輸出,就說明您圓滿完成了所有任務:


清單 19. 顯示 InputArguments
___FCKpd___18

有關 GroovyMBean 的更多信息,請參閱 參考資料。





結束語

Grails 是已經可以在企業中使用。常用企業庫,比如 JMX、Spring 和 log4j 都可以在 Grails 中使用,因為雖然表面不像,但您仍然是進行傳統的 Java EE 開發。

本文是 Trip Planner 應用程序專欄今年的最後一篇文章。我希望保持這系列文章的主題的一致性,以重點講解核心 Grails 功能。在明年的專欄中,我將繼續保留這種風格,但同時我還想納入各種 Grails 應用程序,以擴大視野。

例如,下個月我將介紹一種新的博客系統。您學習關於如何啟動新的 Grails 應用程序的新技術,但它絕不是 “新瓶裝舊酒”。您將重溫 Grails 的 RESTful,但是是在設置完全 Atom 基礎設施的環境中。您將再次使用 JSON 和 Ajax,??過這次是啟用日程表和標誌雲。幾個月之後,我將使用另一種新思維。

Grails 繼續獲得了每個新 Web 站點的大力支持。成熟 Web 框架的標誌是能夠以多種方式使用它。明年的精通 Grails 文章將演示 Grails 中可能實現的各種 Web 站點。到那時,您將享受到精通 Grails 帶來的樂趣。 (責任編輯:A6)






[火星人 via ] 精通 Grails: 在企業中使用 Grails已經有306次圍觀

http://www.coctec.com/docs/linux/show-post-68992.html