天天看點

Windchill的JSP頁面跳轉到Ext頁面的分析

今天花了一天時間,看了一塊代碼,低效但是不得不看懂。

具體内容是這樣的:

Windchill中按鈕菜單欄嵌入了一個菜單項,點選它會觸發彈出一個新的浏覽器頁面,相當于彈框,讓你輸入内容,然後點選儲存按鈕儲存輸入資訊。

問題就在于菜單項本身是一個JSP頁面,點選以後彈出窗體本身會是一個Ext的頁面。

上圖描述一下:

Windchill的JSP頁面跳轉到Ext頁面的分析
上圖這樣的一個菜單項
Windchill的JSP頁面跳轉到Ext頁面的分析

這樣配置的一個菜單項

點選菜單項,也就是這個url以後會跳轉到這個url位址代表的jsp頁面

JSP的代碼如下:

<%@page import="com.bplead.drawingApproval.util.QsWebUtil"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
    //重新定向action位址
    //QsWebUtil.forceNavigateNetmarketsURL(request, response);
      QsWebUtil.BbURL(request, response);
    //---------------//
    //擷取對象oid
    String oid = request.getParameter("oid");
    //擷取action type(編制、檢視)
    String type = request.getParameter("type");
    if("view".equals(type)){
        type = "檢視";
    }else if("edit".equals(type)){
        type = "編制";
    }
%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>圖紙釋出審批流程 </title>
<!-- ExtJS4.2 css-->
<link rel="stylesheet" type="text/css" href="/Windchill/netmarkets/javascript/bplead/extjs-4.2/resources/css/ext-all.css"></link>
<!-- Util CSS -->
<link rel="stylesheet" type="text/css" href="app/css/util.css"></link>
<!-- ExtJS4.2 -->
<script src="/Windchill/netmarkets/javascript/bplead/extjs-4.2/bootstrap.js"></script>
<script src="/Windchill/netmarkets/javascript/bplead/extjs-4.2/locale/ext-lang-zh_CN.js"></script>
<!-- Custom App -->
<script src="app.js"></script>
<!-- JSP Params -->
<script type="text/javascript">
    var oid = '<%=oid%>';
    var type = '<%=type%>';
</script>
</head>
<body>           

上述代碼中的BbURL方法的代碼如下:

public static boolean BbURL(HttpServletRequest request, HttpServletResponse response) throws Exception{
        
        String key = SB_MARKS;//一個無關緊要的普通字元串
        boolean hasRedirect = request.getSession().getAttribute(key) != null;
        
        if (!hasRedirect) {
            
            request.getSession().setAttribute(key, new Object());
            response.sendRedirect(request.getRequestURI() + "?" + request.getQueryString());
            
        } else {
            
            request.getSession().removeAttribute(key);
        }
            return !hasRedirect;
    }           

上述代碼中的BbURL()方法究竟起什麼作用呢?

經過測試搞清楚了:

上述代碼中的if和else代碼塊,在正常的通路中,會分别進入1次,也就是上述BbURL()方法一共被通路2次。

第一次,JSP頁面首次加載,目的是JSP頁面中的java代碼擷取type和oid這兩個java變量的值,然後将java變量的值指派給javascript的對應兩個變量

第一次進入JSP頁面的核心語句就是下面這一段

<script type="text/javascript">
    var oid = '<%=oid%>';
    var type = '<%=type%>';
</script>           

第二次進入JSP頁面的原因就是BbURL()方法,因為這個方法的java代碼也就是If代碼塊中調用了

response.sendRedirect(request.getRequestURI() + "?" + request.getQueryString());           

這句代碼實際上是一句廢話,目的是讓同樣的JSP頁面被第二次通路。

在第二次通路JSP頁面的時候,BbURL()方法進入else代碼塊,實際上并沒有觸發任何有實際意義的業務代碼。

但是第二次通路JSP頁面的時候,由于此時擁有了第一次通路時擷取到的兩個javascript變量:type和oid,是以第二次通路就可以順利将這兩個參數傳遞給ext的js代碼,生成網頁。

上述邏輯,記錄一下。

上述内容總體來說,是無知的闡述,下面結合下一篇博文,詳細闡述一下Windchill系統中的jsp頁面跳轉以及windchill與spring的關系

簡單的說,Windchill的web.xml檔案中配置了spring和springmvc。

Spring的配置檔案較為簡單,僅僅注冊了1個ptc封裝的bean,我估計用途就是,為日後java二次開發的時候,依靠依賴注入将這個bean導入到java代碼中,用來後續通過這個bean通路windchill的資料庫之類的。

SpringMVC的配置檔案比較複雜,這個配置檔案導入了133個其他的xml配置檔案,非常壯觀。

Windchill的JSP頁面跳轉到Ext頁面的分析

說了這麼多,重點在于,springmvc的配置檔案在web.xml中是怎麼配置的呢?換句話說,web.xml檔案中,有哪些和web通路有關的配置呢?這是我們考慮頁面跳轉的關鍵:

首先web.xml檔案中配置了如下内容,也就是說:

所有通路路徑中包含/ptc1/*的url的請求都會被springmvc單獨處理

當然,具體怎麼處理的通過這133個配置檔案也許能找到頭緒

<servlet>
    <description>MVC Dispatcher Servlet</description>
    <servlet-name>MVCDispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextClass</param-name>
      <param-value>com.ptc.mvc.components.support.ComponentXmlWebApplicationContext</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>MVCDispatcher</servlet-name>
    <url-pattern>/servlet/WizardServlet/*</url-pattern>
    <url-pattern>/servlet/ActionsMenu/*</url-pattern>
    <url-pattern>/servlet/RecentList/*</url-pattern>
    <url-pattern>/servlet/Navigation/*</url-pattern>
    <url-pattern>/servlet/SuggestServlet/*</url-pattern>
    <url-pattern>/servlet/TypeBasedIncludeServlet/*</url-pattern>
    <url-pattern>/servlet/UIValidationAJAXServlet/*</url-pattern>
    <url-pattern>/ptc1/*</url-pattern>
    <url-pattern>/app/*</url-pattern>
    <url-pattern>/gwt/*</url-pattern>
  </servlet-mapping>           

我們嘗試在Windchill伺服器的springmvc目錄中檢索133個配置檔案中,哪些對ptc1這個字元串進行了配置

在上述目錄中執行指令grep -nr "component-scan" *

下圖所示的内容,就是這133個檔案中都掃描了那些地方,這些地方就是controller的所在,也就是說,如果url中出現了ptc1,那麼下面這些位置的controller中的java代碼回針對性的對ptc1進行比對,然後進行相應。但是這種分析是徒勞的,因為下圖所示的所有包以及其中的java檔案都是不公開的,而且也實在太多了。

Windchill的JSP頁面跳轉到Ext頁面的分析

以上分析說明,妄圖去浩如煙海的java類中找到對ptc1這個字元串比對的後續的service dao的處理代碼,是徒勞的。

而且即使分析出ptc1字元串的處理代碼,也是舍本求末,因為老大,你自己的二次開發代碼壓根沒有主動向帶有ptc1的url位址發送請求,你首先要搞清楚你的頁面為什麼會指向帶有ptc1字樣的url

是以我們繼續分析web.xml檔案,找到了如下代碼:

<filter>
    <description>Filter for converting Netmarkets URL's with 7.0 or 8.0 JSP's names to URL's having 10.0 JSP's names.</description>
    <filter-name>NetmarketsRedirectFilter</filter-name>
    <filter-class>com.ptc.core.components.filter.NetmarketsRedirect</filter-class>
  </filter>
    <filter-mapping>
    <filter-name>NetmarketsRedirectFilter</filter-name>
    <url-pattern>/netmarkets/jsp/*</url-pattern>
  </filter-mapping>           

上述代碼說明,web.xml中為Windchill配置了filter

隻要主動通路windchill下/netmarkets/jsp/這個目錄下的http request都會被com.ptc.core.components.filter.NetmarketsRedirect這個filter過濾一下

那我們最初關注的url哈記得是什麼樣子嗎?就是Windchill中通過配置檔案二次開發的按鈕,我們看看

Windchill的JSP頁面跳轉到Ext頁面的分析

可以很清楚的看到,我們自己配置的url是:

url="/netmarkets/jsp/bplead/drawingApproval/drawingApproval.jsp?type=edit"           

也就是我們主動通路的/netmarkets/jsp/目錄下的位址

那麼依據web.xml檔案中filter的配置,這個request一定會被com.ptc.core.components.filter.NetmarketsRedirect這個filter過濾一下

下面我們看看這個filter的内容

Windchill的JSP頁面跳轉到Ext頁面的分析
Windchill的JSP頁面跳轉到Ext頁面的分析
Windchill的JSP頁面跳轉到Ext頁面的分析

如上圖所示,web.xml檔案中将NetmarketsRedirect類注冊為url特征為/netmarkets/jsp/*的filter。ptc自行實作了Filter名為JCAFilter,而NetmarketsRedirect類是JCAFilter的子類。

當/netmarkets/jsp/*類型的url被web.xml檔案中注冊的filter過濾後,依據filter的規則,會調用NetmarketsRedirect類的dofilter()方法,由于NetmarketsRedirect類是JCAFilter的子類,是以就會調用它的dofilter()方法,如下圖:

public abstract class JCAFilter implements Filter{

public void doFilter(ServletRequest paramServletRequest, ServletResponse paramServletResponse, FilterChain paramFilterChain)
    throws IOException, ServletException
  {
    paramServletRequest.setCharacterEncoding("UTF-8");
    paramServletResponse.setContentType("text/html; charset=UTF-8");
    
    if ((!(paramServletRequest instanceof HttpServletRequest)) || (!(paramServletResponse instanceof HttpServletResponse))) {
      log.debug("Request/Response was not HttpServlet");
      paramFilterChain.doFilter(paramServletRequest, paramServletResponse);
      return;
    }
    if (log.isDebugEnabled()) {
      log.debug("Requested " + ((HttpServletRequest)paramServletRequest).getRequestURL());
    }
    HttpServletRequest localHttpServletRequest = (HttpServletRequest)paramServletRequest;
    //
    String str = getURLForRedirect(localHttpServletRequest);
    
    }//doFilter()方法結束
    
    //父類中隻有一個抽象方法,NetmarketsRedirect類繼承後再實作
  protected abstract String getURLForRedirect(HttpServletRequest paramHttpServletRequest);
  
  public void destroy() {}
  
  public void init(FilterConfig paramFilterConfig)
    throws ServletException
  {}
    
    }//JCAFilter類結束           

上述代碼是JCAFilter類的部分代碼,可以看到doFilter()方法的核心就是調用getURLForRedirect()方法。

那麼NetmarketsRedirect類中getURLForRedirect()方法的内容是怎樣的呢?

public class NetmarketsRedirect
  extends JCAFilter
{
  @SuppressWarnings(value={"BC_IMPOSSIBLE_CAST"}, justification="String[] portletValues = (String[])qs.get(NmContextBean.PORTLET) is possible cast becuase, the map adds string array as value")
  protected String getURLForRedirect(String paramString1, String paramString2, String paramString3)
  {
    String str1 = null;
    
    if ((paramString1.contains("ptc1")) || (paramString1.contains("app/#"))) {
      log.debug("No redirect, the url is already an X-20 style");
      return null;
    }
    if (!paramString1.endsWith(".jsp")) {
      log.debug("No redirect, this filter doesn't handle cases that don't end with \".jsp\"");
      return null;
    }
    
    if (paramString1.endsWith("/work/list.jsp")) {
      log.debug("Home -> Assignments");
      str1 = getNewURL("work/listAssignments", paramString2);
    } else if (paramString1.endsWith("/netmarkets/overview.jsp")) {
      log.debug("Home Overview");
      
      str1 = getNewURL("homepage", null);
    } else if (paramString1.endsWith("/events/list_mine.jsp")) {
      log.debug("Home -> Meetings");
      str1 = getNewURL("meeting/list_mine", paramString2);
    } else if (paramString1.endsWith("/subscription/list.jsp")) {
      log.debug("Home -> Subscriptions");
      str1 = getNewURL("subscription/listSubscriptions", paramString2);
    } else if ((paramString1.endsWith("/work/listProduct.jsp")) || (paramString1.endsWith("/work/listProduct2.jsp"))) {
      log.debug("Product -> Assignments");
      str1 = getNewURL("work/listProductAssignments", paramString2);
    } else if (paramString1.endsWith("listFiles.jsp")) {
      log.debug("Folder Browser listFiles.jsp");
      HashMap localHashMap = NmURLFactoryBean.parseQueryString(paramString2);
      
      String[] arrayOfString = (String[])localHashMap.get("portlet");
      String str2 = arrayOfString == null ? null : arrayOfString[0];
      if (!"component".equals(str2)) {
        str1 = getNewURL(NmURLFactoryBean.relativizeUrl(paramString1, false), paramString2);
      }
    } else if ((paramString1.endsWith("/folder/view.jsp")) || (paramString1.endsWith("/project/view.jsp"))) {
      log.debug("Folder Info Pages");
      str1 = null;
      
      if (paramString3.indexOf("PDMLinkProduct") > 0) {
        log.debug("Product -> Folder Info Page");
        str1 = getNewURL("product/listFiles", paramString2);
      } else if (paramString3.indexOf("Project2") > 0) {
        log.debug("Project -> Folder Info Page");
        str1 = getNewURL("project/listFiles", paramString2);
      } else if (paramString3.indexOf("WTLibrary") > 0) {
        log.debug("Library -> Folder Info Page");
        str1 = getNewURL("library/listFiles", paramString2);
      } else if (paramString3.indexOf("SubFolder") > 0) {
        log.debug("Folder Info Page");
        str1 = getInfoPageURL(paramString2);
      } else {
        log.debug("This case is not covered. Do not redirect.");
        return null;
      }
    } else if (paramString1.endsWith("/project/list3.jsp")) {
      log.debug("Project -> Project List (7.0 only)");
      str1 = getNewURL("project/list", null);
    } else if (paramString1.endsWith("/projectResource/list.jsp")) {
      log.debug("Project -> Resources (7.0 only)");
      str1 = getNewURL("project/listProjectResource", paramString2);
    } else if (paramString1.endsWith("/work/listProject.jsp")) {
      log.debug("Project -> Assignments");
      str1 = getNewURL("work/listProjectAssignments", paramString2);
    } else if (paramString1.endsWith("/report/projectReport.jsp")) {
      log.debug("Project -> Reports (7.0 only)");
      str1 = getNewURL("project/reports", paramString2);
    } else if (paramString1.endsWith("/wtcore/jsp/com/ptc/windchill/search/ExecuteSearch.jsp")) {
      log.debug("All -> Search");
      str1 = getNewURL("search/executeSearch?", null);
    } else if (paramString1.endsWith("/change/list3.jsp")) {
      log.debug("Change -> Problem Reports");
      str1 = getNewURL("change/list", null);
    } else if (paramString1.endsWith("/change/listChangeRequests2.jsp")) {
      log.debug("Change -> Change Requests");
      str1 = getNewURL("change/listChangeRequests", null);
    } else if (paramString1.endsWith("/change/listChangeNotices2.jsp")) {
      log.debug("Change -> Change Notices");
      str1 = getNewURL("change/listChangeNotices", null);
    } else if ((paramString1.endsWith("/work/listLibrary.jsp")) || (paramString1.endsWith("/work/listLibrary2.jsp"))) {
      log.debug("Library -> Assignments");
      str1 = getNewURL("work/listLibraryAssignments", paramString2);
    } else if (paramString1.endsWith("/library/list2.jsp")) {
      log.debug("Library -> Libraries List");
      str1 = getNewURL("library/list", paramString2);
    } else if (paramString1.endsWith("/site/listOrgs.jsp")) {
      log.debug("Site -> Organizations");
      str1 = getNewURL("org/listOrgs?tab=org", null);
    } else if (paramString1.endsWith("/org/view.jsp")) {
      log.debug("Org info page");
      str1 = getNewURL("org/details", paramString2);
    } else if ((paramString1.endsWith("/view.jsp")) && (
      (paramString1.endsWith("/part/view.jsp")) || (paramString1.endsWith("/document/view.jsp")) || 
      (paramString1.endsWith("/bookmark/view.jsp")) || (paramString1.endsWith("/milestone/view.jsp")) || 
      (paramString1.endsWith("/projectActivity/view.jsp")) || 
      (paramString1.endsWith("/summaryActivity/view.jsp")) || 
      (paramString1.endsWith("/projectProxy/view.jsp")) || (paramString1.endsWith("/meeting/view.jsp")) || 
      (paramString1.endsWith("/actionitem/view.jsp")) || (paramString1.endsWith("/epmdocument/view.jsp")))) {
      log.debug("Project Link info pages");
      
      str1 = getInfoPageURL(paramString2);
    } else {
      log.debug("This URL doesn't contain an oid, so we don't need to do anything.");
      return null;
    }
    return str1;
  }           

上述代碼其實調用了很多ptc自己封裝的内容,至于具體這個filter到底做了什麼,對url做了什麼處理,我們編寫測試代碼檢視一下。

我們修改web.xml配置1個單獨的filter然後配置1個自定義的servlet

然後我們用浏覽器通路servlet和filter對應的url,這樣,我們在filter的代碼中再現NetmarketsRedirect類的語句以及它引用的語句,這樣我們就能搞清楚,Windchill中所謂重定向,在搞什麼鬼了。

我們在自己的eclipse上的windchill項目上測試servlet和filter

Windchill的JSP頁面跳轉到Ext頁面的分析
Windchill的JSP頁面跳轉到Ext頁面的分析
Windchill的JSP頁面跳轉到Ext頁面的分析
Windchill的JSP頁面跳轉到Ext頁面的分析

在eclipse中建立servlet後,eclipse會幫助你在web.xml檔案自動添加相關代碼:

Windchill的JSP頁面跳轉到Ext頁面的分析

這并非我們想要的,是以修改一下:

Windchill的JSP頁面跳轉到Ext頁面的分析

servlet中的核心方法的代碼如下:

/**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        response.getWriter().append("Served at: ").append(request.getContextPath());
    }           

我們先測試一下效果:

Windchill的JSP頁面跳轉到Ext頁面的分析

說明servlet正常運作

下面建立一個filter