世事如棋
          Aspire to Professionalism
          posts - 4,  comments - 12,  trackbacks - 0
          Apache Pluto 是 Java? 開發人員可以自由使用的工具,用來測試他們的 portlets 是否遵循 JSR 168 Portlet 規范。開發人員 Mark Talbot 和 Kulvir Singh Bhogal 帶領您學習 portlet 測試。您將安裝 Pluto,構建、編譯和打包一個簡單的 portlet,并將它部署到 Pluto 以測試它的 JSR 168 兼容性。您還將了解到 Portlet 規范的下一個版本將保留哪些內容。

          2003 年 10 月,Java Community Process 發布了 Java Specification Request (JSR) 168的最終版本:Portlet Specification(參見 參考資料)。JSR 168 闡述的是第一個 Java portlet 開發的編程標準。以前,為 WebSphere Portal Server 開發的 portlet 不能在另外的 portlet 容器(例如 BEA WebLogic Portal)中運行。Portlet 容器不是 J2EE 應用服務器所必需的組件。然而,這種可移植性的缺少是與標準 J2EE 企業級應用程序相背離的,標準 J2EE 企業級應用程序(當根據規范構建時)可以部署到任何與 J2EE 兼容的應用服務器中。缺少 portlet 可移植性和相關廠商的鎖定阻止企業購買門戶服務器。通過結束 portlet 開發的混亂狀態,JSR 168 平息了那些企業的擔憂。

          JSR 168 Portlet API 是相當廣泛的。本文不打算成為 JSR 168 的教程,因此我們將不去深入鉆研這些 API。如果您不熟悉 JSR 168 API, 建議您花一些時間去熟悉 參考資料 中的一些材料。

          Java 開發人員具有一個可以自由獲得的工具,用來測試他們編寫的 portlets 是否與 Portlet 規范相一致。Apache Pluto 是 JSR 168 的參考實現,是實現了 Portlet API 的 portlet 容器。像 Pluto 和 IBM WebSphere Portal Server 這樣的 portlet 容器充當 portlets 的運行時環境,與 web 應用服務器的 servlet 容器的運行時環境支持 servlet 的情形非常相似。但是,portlet 容器不是獨立的,它存在于 servlet 容器的頂部并依賴于它的服務。在本文中,我們將為您演示如何編寫簡單的 portlet 并使用 Pluto portlet 容器測試它。

          安裝 Pluto

          Apache Pluto 需要 Java SE 5。如果您還沒有安裝此版本的 JDK,下載并安裝它以繼續進行本文的練習(參見 參考資料)。

          如果 JAVA_HOME 已經設置為在您的系統上安裝的先前版本的 JVM, 更改它會使已有的依賴 JVM 的應用程序崩潰。如果已經安裝了老版本的 JVM,您應該直接在 Pluto 啟動腳本中更新 JAVA_HOME 變量: Windows 系統上的 startup.bat 或者 Linux 上的 startup.sh。

          接下來,需要設置和更改 JAVA_HOME 環境變量,Pluto 引用它來查找 JVM(默認情況下,Sun 的安裝程序不設置 JAVA_HOME 的值)。如果在系統上沒有安裝以前版本的 JVM,則執行以下步驟(這些說明假定您運行的是 windows):

          1. 右擊我的電腦,并從上下文菜單選擇屬性
          2. 單擊高級選項卡。
          3. 在窗口的底部,單擊環境變量按鈕。
          4. 系統變量窗格中,單擊新建
          5. 為變量名輸入 JAVA_HOME。為變量值輸入 Sun JVM 的安裝目錄。

          圖 1 展示了選擇把 Java 5.0 SDK 安裝到 C:\Program Files\IBM\Java50 目錄下時 JAVA_HOME 的值:


          圖 1. 更改 JAVA_HOME 環境變量

          現在已經正確地設置了 Java 環境,就來從 Apache 的 Web 站點(參見 參考資料)下載包含 Pluto 的二進制內容的 ZIPet 文件(pluto-current.zip)。

          驗證下載文件

          假定您和我們的安全意識一樣強,就會希望保證下載的 ZIP 文件確實來自 Apache Software Foundation。文件使用 PGP 和 MD5 進行了數字簽名。使用 GnuPG(參見 參考資料)和 ASCII armor 文件(pluto-current.zip.asc),可以驗證此下載文件的真實性。

          首先,通過輸入清單 1 頂部一行所示的命令,把 Apache Pluto 的公共密鑰添加到您的公共密鑰環。在 Pluto 下載頁面可以獲得 KEYS 文件。


          清單 1. 導入 KEYS 文件
          												
          														C:\>gpg --import KEYS
          gpg: key E41EDC7E: public key "Carsten Ziegeler <cziegeler@apache.org>" importe
          
          gpg: key 320B4FB4: public key "Nick Lothian (Apache) <nlothian@apache.org>" imp
          rted
          gpg: key DD4200EA: public key "David H. DeWolf <david@daviddewolf.com>" importe
          
          gpg: Total number processed: 3
          gpg:               imported: 3
          gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
          gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
          gpg: next trustdb check due at 2011-03-08
          
          												
          										

          接下來,輸入清單 2 頂部一行所示的命令來驗證您從 Apache 接收的已簽名的 ZIP 文件:


          清單 2. 驗證 pluto-1.0.1.zip.asc
          												
          														C:\>gpg --verify pluto-1.0.1.zip.asc
          gpg: Signature made 10/19/05 09:11:31 using DSA key ID DD4200EA
          gpg: Good signature from "David H. DeWolf <david@daviddewolf.com>"
          gpg:               aka "David H. DeWolf <ddewolf@apache.org>"
          gpg:               aka "David H. DeWolf <ddewolf@rocketmail.com>"
          gpg:               aka "David H. DeWolf <ddewolf@gmail.com>"
          gpg:               aka "David H. DeWolf <david.dewolf@digitalfocus.com>"
          gpg: WARNING: This key is not certified with a trusted signature!
          gpg:          There is no indication that the signature belongs to the owner.
          Primary key fingerprint: 6AA5 5850 9A7B 275C E0BC  2851 B217 63FA DD42 00EA
          
          												
          										

          如果 gpg --verify 命令生成的輸出與清單 2 顯示的相似,就可以確信 Pluto 歸檔文件確實來自 Apache。可以忽略表明簽名不可信的警告。保證公共密鑰簽名來自所有者的惟一方法是,密鑰的所有者親自把磁盤上的密鑰交給您。但是,采取這些步驟以后,可以在某種程度上確定從 Apache 下載的 ZIP 文件是不用懷疑的。(有關驗證 Apache 下載文件的更多信息,請參見 參考資料 。)

          解壓 Pluto 下載文件

          現在準備把 ZIP 文件解壓到您的機器上。我們假定您把 Pluto 解壓到 C:\ 盤的根目錄上:

          												
          														C:\>unzip pluto-current.zip
          
          												
          										

          這就創建了一個包含 bin 子目錄的 C:\pluto-1.0.1 目錄。導航到 bin 子目錄并在命令提示符下輸入 startup.bat 以啟動 Pluto 服務器。

          如圖 2 所示,您可以從 Pluto 主頁管理和查看 portlets。啟動 Web 瀏覽器并導航到 http://localhost:8080/pluto/portal。


          圖 2. Pluto 主頁
          Pluto 主頁

          注意:如果 Pluto 主頁沒有出現,請確保防火墻沒有阻止 Pluto 接受連接。

          現在已經啟動和運行 Pluto,您將創建一個簡單的 portlet,然后使用 Pluto 測試它的 JSR 168 兼容性。





          回頁首


          開發 JSR 168 portlet

          為了查看如何使用 Pluto 作為 portlet 的 JSR 168 兼容性測試平臺,需要一個 portlet 來進行測試。在此練習中,您將創建一個簡單的 portlet,它根據用戶的輸入,把文本框中的內容轉換為全是大寫字符或全是小寫字符(見圖 3):


          圖 3. Change Case portlet

          我們從查看清單 3 所示的 portlet.xml 文件開始。portlet.xml 文件是包含關于 WAR 文件中捆綁的 portlet 的配置細節的描述符文件。


          清單 3. portlet.xml 文件
          												
          														<?xml version="1.0" encoding="UTF-8"?>
          <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" 
             version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
             xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd 
             http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" 
             id="com.ibm.changecase.ChangeCasePortlet">
             <portlet>
                <portlet-name>ChangeCase</portlet-name>
                <display-name>Change Case Portlet</display-name>
                <portlet-class>com.ibm.changecase.ChangeCasePortlet</portlet-class>
                <supports>
                   <mime-type>text/html</mime-type>
                   <portlet-mode>view</portlet-mode>
                </supports>
                <portlet-info>
                   <title>ChangeCasePortlet</title>
                </portlet-info>
             </portlet>
          </portlet-app>
          
          												
          										

          清單 3 中的 <portlet-app> 標記定義了 XML 模式定義和 portlet 應用程序 的 ID。一個 portlet 應用程序可以包含零個或多個 portlet。使用 <portlet> 標記來定義 portlet 應用程序中的單個 portlet:

          • <portlet-name> —— 提供一個名稱,在內部或由程序使用該名稱來引用 portlet。
          • <display-name> —— portlet 的縮寫名,用來在 GUI 工具中顯示 portlet 名稱,它隨 portlet 容器的不同而不同。
          • <portlet-class> —— 充當 portlet 控制器的類。
          • <supports> —— 這些標記定義 portlet 支持的 portlet 模式和 mime 類型。
          • <title> —— 可以在 portlet.xml 中定義 portlet 的首選標題。但是,如何使用該標題取決于 portlet 容器。

          清單 4 顯示的是 portlet.xml 中引用的 com.ibm.changecase.ChangeCasePortlet portlet 類。此類必須實現 javax.portlet.Portlet 接口,但幸運的是,您不必直接實現 Portlet 接口。JSR 168 為 javax.portlet.Portlet 接口定義了一個稱為 javax.portlet.GenericPortlet 類的默認實現。com.ibm.changecase.ChangeCasePortlet 類繼承 GenericPortlet 類。


          清單 4. ChangeCasePortlet 類
          												
          														package com.ibm.changecase;
          
          import java.io.*;
          import javax.portlet.*;
          
          /**
           *
           * A sample portlet based on GenericPortlet
           * 
           */
          public class ChangeCasePortlet extends GenericPortlet {
             
             private static String VIEW_JSP = "/view.jsp";
          
             protected void doView(RenderRequest request, RenderResponse response) 
                throws PortletException, IOException {
                response.setContentType(request.getResponseContentType());
                 PortletContext context = getPortletConfig().getPortletContext();
                 context.getRequestDispatcher(VIEW_JSP).include(request, response);
             }
          
          public void processAction(ActionRequest request, ActionResponse response) 
             throws PortletException, java.io.IOException {
          //Do Action Handling here.
          
          }
          
          }
          
          												
          										

          注意重寫 doView()processAction() 方法的選擇。每當出現 portlet 操作時都會調用 processAction() 方法。當用戶處于 portlet 的視圖模式時調用 doView() 方法。JSR 168 支持其他的模式,例如幫助模式和編輯模式。但是,如果回過頭看一下 清單 3,在 <supports> 部分可以看到 portlet 只支持視圖模式。

          現在仔細地看一下清單 4 中的 doView() 方法。與 Java servlet 編碼中一樣,portlet 編碼中也經常使用模型-視圖-控制器 (MVC) 設計模式。因此,在 portlet 中,把表示的職責轉交給了 view.jsp。或者,也可以使用 prinltn 邏輯在 doView() 方法中實現視圖。

          												
          														response.getWriter().println("<p>Hello World</p>()");
          
          												
          										

          這種方法的問題是,圖形用戶界面的設計者需要具有 portlet 技術的知識來編寫 doView() 方法。JSP 開發人員從復雜的 Java 開發中解放出來,能夠集中精力開發前端界面。清單 5 顯示的是 view.jsp:


          清單 5. view.jsp
          												
          														<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet"%>
          <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
          	pageEncoding="ISO-8859-1" session="false"%>
          <portlet:defineObjects />
          
          <%String textBox = renderRequest.getParameter("textBox");
          		if (textBox == null)
          			textBox = "";
          		String caseString = renderRequest.getParameter("case");
          		boolean isUpperCase = true;
          		if ((caseString != null) && (caseString.equals("false"))) {
          			isUpperCase = false;
          		}
          		String errorMessage = renderRequest.getParameter("errorMessage");%>
          
          <%if (errorMessage != null) {%>
          <p><%=errorMessage%></p>
          <%}%>
          
          <FORM name="<portlet:namespace/>caseform" action="<portlet:actionURL/>">
          <INPUT type="text" name="textBox" size="20" value="<%=textBox%>">
          <p><INPUT type="radio" name="case" value="lowercase"
          	<%if (!isUpperCase) {%> checked="checked" <%}%>>To Lowercase</p>
          <p><INPUT type="radio" name="case" value="uppercase"
          	<%if (isUpperCase) {%> checked="checked" <%}%>>To Uppercase</p>
          <INPUT type="submit" name="<portlet:namespace/>submitCase"
          	value="Change Case"></FORM>
          
          												
          										

          首先,注意在 view.jsp 中定義 portlet 標記庫。這是 JSP 解析器識別 portlet 標記所必需的。您使用的第一個 portlet 標記是 <portlet:defineObjects/>。此標記允許訪問 renderRequestrenderResponseportletConfig 對象。使用 renderRequest 對象使您具有訪問 requestParameters 的權利。portlet 類通過 request 參數向 view JSP 傳遞值。

          接下來,在 view.jsp 中創建一個向 portlet 類發送表單數據的表單。為了發送表單數據,必須創建一個 actionURL,它使 ChangeCasePortlet portlet 類的 processAction() 方法被調用。使用 <portlet:actionURL/> 標記創建 actionURL。 注意,在 view.jsp 中將文本框和單選按鈕的值設置為服務器傳回 JSP 的值。因此,view.jsp 負責處理請求輸入和顯示 portlet 的響應。

          單擊表單的 Submit 按鈕會調用 portlet 的 processAction() 方法,如清單 6 所示。processAction() 從 view.jsp 接收一個 ActionRequest 對象作為輸入。


          清單 6. processAction 方法
          												
          														 public void processAction(ActionRequest request, ActionResponse response) 
              throws PortletException, java.io.IOException {
              String newCase = request.getParameter("case");
              String textBox = request.getParameter("textBox");
              String errorMessage = null;
                
              boolean isUpperCase = true;
              if ((newCase!=null) && (newCase.equals(ChangeCaseConstants.LOWER_CASE)))
                 isUpperCase = false;
              else if ((newCase == null) || (newCase==ChangeCaseConstants.UPPER_CASE))
                 errorMessage = "Error no case selected!  Select a case.";
              if (textBox !=null) {
                 if (isUpperCase)
                    textBox = textBox.toUpperCase();
                 else 
                    textBox = textBox.toLowerCase();
                 response.setRenderParameter("textBox", textBox);
              } else 
                 errorMessage = "Error, text in the text box is invalid";
              response.setRenderParameter("case", Boolean.toString(isUpperCase));
              if (errorMessage != null) {
                 response.setRenderParameter("errorMessage",errorMessage);
              }
             
           }
          
          												
          										

          ActionRequest 對象包含輸入到表單中的數據。為了檢索表單數據,可使用 getParameter() 方法。在 processAction() 方法中,也要執行業務邏輯,確定用戶是想要大寫形式還是小寫形式的輸出。根據該邏輯,把輸入的文本轉換成想要的大小寫形式并發送給用戶。使用 setRenderParameter() 方法把數據發送給視圖。





          回頁首


          編譯并打包 JSR 168 portlet

          現在已經開發了 portlet,需要把它轉換成已編譯的形式,并為了部署到 Pluto 將它打包。首先,確保 portlet-api-1.0.jar 在 CLASSPATH 中。然后使用 javac 編譯器編譯 ChangeCaseConstants.java 和 ChangeCasePortlet.java:

          												
          														javac ChangeCaseConstants.java
          javac ChangeCasePortlet.java
          
          												
          										

          接著需要為 WAR 文件創建所需要的文件夾結構,WAR 文件是歸檔文件,通過它把 portlet 部署到 portlet 容器。把剛才編譯的兩個類放在 classes\com\ibm\changecase 目錄中。

          為了構建 WAR 文件,需要以下的目錄結構,如清單 7 所示:


          清單 7. 用于部署的目錄結構
          												
          														changeCaseWAR\
             META-INF
                MANIFEST.MF
             WEB-INF
                classes
                   com
                      ibm
                         changecase
                            ChangeCaseConstants.class
                            ChangeCasePortlet.class
                lib
                tld
                   portlet.tld
                portlet.xml
                web.xml
             index.html
             view.jsp
          
          												
          										

          注意,清單 7 引入了 4 個我們還沒有討論過的文件:

          • portlet.tld:這是 portlet 標記庫。如果回想一下,您曾經用 <portlet:defineObjects /> 之類的引用在整個 JSP 中都用到過 portlet 標記庫。portlet 標記庫在 Apache-SVN 代碼庫中可以得到(參見 參考資料)。

          • MANIFEST.MF:因為這里不存在外部依賴,所以這個清單文件除了 Manifest-Version: 1.0 之外什么也不包含。

          • index.html:如果由于某些原因上下文根被直接訪問,則會顯示 index.html。在 index.html 中可以有任何正確格式的 HTML。

          • web.xml(如清單 8 所示):它定義包含單個 portlet 的 Web 應用程序。

          清單 8. web.xml
          												
          														<?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" 
             "http://java.sun.com/dtd/web-app_2_3.dtd">
          <web-app id="WebApp_ID">
             <display-name>CasePortlet</display-name>
             <welcome-file-list>
                <welcome-file>index.html</welcome-file>
             </welcome-file-list>
             <taglib>
                <taglib-uri>http://java.sun.com/portlet</taglib-uri>
                <taglib-location>tld/portlet.tld</taglib-location>
             </taglib>
             <taglib id="PortletTLD">
                <taglib-uri>http://java.sun.com/portlet</taglib-uri>
                <taglib-location>/WEB-INF/tld/std-portlet.tld</taglib-location>
             </taglib>
          </web-app>
          
          												
          										

          一旦生成了 MANIFEST.MF、portlet.tld、web.xml 和 index.html,就可以使用 JAR 實用工具將清單 7 所示的結構歸檔到一個 WAR 文件中:

          												
          														C:\temp>jar -cvf changeCase.war
          
          												
          										

          結果是一個叫做 changeCase.war 文件,用于部署到 JSR 168 兼容的門戶。





          回頁首


          確保 portlet 是 JSR 168 兼容的

          現在使用 Apache Pluto 來查明 portlet 是否能通過 JSR168 兼容性的最終測試。

          確保 Apache Pluto 啟動并運行,然后導航到 Pluto 主頁(http://localhost:8080/pluto/portal)。單擊側欄上的 Admin。應該看到 Deploy War portlet 顯示在管理頁面上,如圖 4 所示。單擊 Browse 并導航到在前一節中放置在一起的 changeCase.war 文件的位置,然后單擊 Submit


          圖 4. Pluto 的 Deploy War portlet

          現在必須為 portlet 應用程序輸入布局信息 —— 一個非常不重要的任務,因為在 portlet 應用程序中只有一個 portlet。正如在圖 5 中可以看到的,您告知 Pluto portlet 有一行和一列,然后單擊 Submit 向服務器提交此選擇:


          圖 5.為 portlet 應用程序輸入頁面布局信息

          接下來,必須定義 portlet 出現在 portlet 應用程序頁面布局的何處。通過把 portlet 應用程序中的所有 portlet 映射到剛才定義的行和列來完成此任務。因為只有一個 portlet 需要部署并且您先前選擇了一行和一列的頁面布局,所以在該位置輸入 ChangeCase portlet 并單擊 Submit,如圖 6 所示:


          圖 6. 映射 portlet 的位置

          為了部署 portlet,您可以選擇重新啟動 Pluto 或者熱部署 包含 portlet 的 portlet 應用程序,如圖 7 所示:


          圖 7. 熱部署 portlet

          一個具有 portlet 應用程序名稱(Change Case)的鏈接出現在側欄。單擊該超鏈接,現在將看到 portlet 應用程序,其中包含 portlet,如圖 8 所示。這時應該與 Change Case portlet 交互并確保它的功能與預期的一樣。如果與預期的一樣,您就可以確定此 portlet 與 JSR 168 兼容。


          圖 8. 部署到 Pluto 的 Change Case portlet

          Change Case Portlet 將能夠運行在任何支持 JSR 168 portlet 標準的 Portlet 容器中。





          回頁首


          結束語

          我可以在生產環境中使用 Pluto 嗎?

          Apache Pluto 只適合作為 JSR 168 兼容性的測試平臺,不推薦在生產環境中使用。對于生產來說,您應該考慮使用更加可伸縮的容器。您可以考慮使用 Apache Software Foundation 的名為 Jetspeed 的開放源碼 portlet 容器(參見 參考資料)。IBM 的 WebSphere Portal Server Version 5.1 也是一個支持 JSR 168 標準的商業解決方案(參見 參考資料)。

          Apache Pluto 使 portlet 開發人員能夠確定他們的 portlet 能夠運行在任何 JSR 168 兼容的 portlet 容器中。大多數 portlet 容器(包括 WebSphere Portal Server)都包括 Portlet 規范未提及的擴展。例如,IBM WebSphere Portal Server 提供非標準的一點即動(click-to-action)擴展(即協作 portlet)。是否使用擴展由使用 portal 技術的企業來決定。作為開發人員,您應該牢記的是支持這種技術會損害 portlet 的可移植性 —— 但是有時這么做能滿足業務需要。Apache Pluto 使那些使用 portlet 的組織能夠知道他們偏離了 Portlet 規范有多遠,并決定是否采取一些措施來調整那些 portlet 符合標準。

          展望未來:JSR 286

          JSR 168 在走向使 portal 領域有序的過程經過了很長的路。但是,portlet 的標準化工作并沒有停止。當寫作本文的時候,Java Portlet API version 2.0 規范 (JSR 286) 仍舊在開發中,打算把對 J2EE 1.4 的支持引入到 portlet 規范中(參見 參考資料)。許多曾由供應商利用其自有實現通過非標準方式處理的 Portlet 技術(提一下其中的兩個,portlet 過濾器和形式化的 portlet 間通信)現將采納 2.0 規范。Apache Pluto 的將來版本將充當 JSR 286 規范的參考實現。當 JSR 286 成為事實上的 portlet 標準時,您將仍舊能夠使用 Pluto 測試兼容性。

          在我們寫作本文時,還不能得到 Pluto 1.1 的 alpha 版本。Pluto 1.1 將具有一個新的容器架構,并包含一些使 portlet 開發更容易的更改。但是,Pluto 1.0.1 仍然是一個很好的用于驗證 portlet 是否 JSR 168 兼容的工具。





          回頁首


          參考資料

          學習

          獲得產品和技術
          posted on 2006-05-28 09:03 KingWell 閱讀(239) 評論(0)  編輯  收藏 所屬分類: Web Service

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          歡迎訪問我的網站
          JSF中國

          <2025年5月>
          27282930123
          45678910
          11121314151617
          18192021222324
          25262728293031
          1234567

          常用鏈接

          留言簿(1)

          隨筆檔案

          文章分類

          文章檔案

          收藏夾

          我的資源

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 凌云县| 辽中县| 兴化市| 海原县| 汉川市| 通渭县| 寿阳县| 内丘县| 阿巴嘎旗| 安新县| 左云县| 大余县| 鄂托克旗| 盱眙县| 阿拉善右旗| 乡宁县| 会泽县| 蚌埠市| 永靖县| 乌海市| 淮滨县| 塘沽区| 临颍县| 离岛区| 长泰县| 六安市| 剑阁县| 武定县| 开鲁县| 阳原县| 丹东市| 彰武县| 呼伦贝尔市| 平和县| 武夷山市| 梅河口市| 嫩江县| 永嘉县| 彭水| 县级市| 濮阳县|