UML软件工程组织

 

 

icai项目开发日记
 
2007-12-11 来源:atom (个人网站)
 

说来可笑,接到这个小项目的时候还不知道什么是icai.后来Google 了一下.ICAI--智能计算机辅助教育系统(Intelligent Computer Assisted Instruction 简称ICAI).这个项目凝聚了兴趣小组的心血.花了不少时间.现在终于成型.虽然有些功能不是很理想,但至少大家一直在努力.这个系统是学习大学本科教材<编译原理>(清华大学出版)后的我们兴趣小组的一次实践.功能主要包括:文法语言,词法分析,语法分析和中间代码生成的常规分析.偶主要负责web页面和web层的集成.在此记录一二,全当备忘

使用工具:eclipse+myeclipse+tomcat+sql server;

使用框架:struts+log4+dbcp;

一.使用tiles 

tiles的典型用法有两种.无论使用哪种方法.tiles是做为一个插件放入到struts应用中的.所以必须在struts-config.xml中插入这个插件通常的配置如下:

<plug-in className="org.apache.struts.tiles.TilesPlugin" >
      
<!-- Path to XML definition file -->
      
<set-property property="definitions-config"
                      value
="/WEB-INF/tiles-defs.xml" />
      
<!-- Set Module-awareness to true -->
      
<set-property property="moduleAware" 
                      value
="true" />
</plug-in>  

第一种用法是先创建一个公用layout.jsp页用来布局.然后在其中引用tiles-defs.xml文件中配置好的元素:
文件layout.jsp:

<table width="100%"  border="0" align="center" cellpadding="0" cellspacing="0" bgcolor="white">
  
<tr>
    
<td width="24%" height="10"> </td>
    
<td width="76%" height="10"><tiles:insert attribute="header"/></td>
  
</tr>
  
<tr>
    
<td width="24%" height="100%" align="right" valign="top"><tiles:insert attribute="sidebar"/></td>
    
<td width="76%" height="100%" align="left"><div id="body"><tiles:insert attribute="content"/> </div></td>
  
</tr>
</table>
<table width="100%" border=0 align="center" cellPadding=0 cellSpacing=0 bgcolor="WHITE" margin-bottom=0>
  
<tr>
    
<td height="64"><tiles:insert attribute="footer"/> </td>
  
</tr>
</table>

文件tiles-defs.xml就像一个零件装配的工厂,通过它可以组装各种网页,这对于见面风格的统一很是方便.重用的是它可以继承已有的组件以实现复用.

<?xml version="1.0" encoding="ISO-8859-1" ?>

 
<!DOCTYPE tiles-definitions PUBLIC
       "-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN"
       "http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd"
>

<tiles-definitions>
   
<definition  name="sidebar-definition"   path="/WEB-INF/tiles/common/sidebar-layout.jsp">
      
<put name="bottom" value="/WEB-INF/tiles/common/sidebar-links.jsp"/>
   
</definition>  
   
<definition  name="base-definition"  path="/WEB-INF/tiles/common/layout.jsp">
      
<put name="top-header"  value="/WEB-INF/tiles/common/top-header.jsp"/>
      
<put name="sidebar" value="sidebar-definition" type="definition"/>
      
<put name="title"  value="/WEB-INF/tiles/common/title.jsp"/>
      
<put name="header"  value="/WEB-INF/tiles/common/header.jsp"/>
      
<put name="content" value=""/>
      
<put name="footer"  value="/WEB-INF/tiles/common/footer.jsp"/>
   
</definition>
<!--
    for defaul web
-->
   
<definition  name="index-definition"   extends="base-definition">
      
<put name="content" value="/WEB-INF/tiles/default/Welcome.jsp"/>   
   
</definition>
</tiles-definitions>

当然用tiles装订的网页是不能直接在地址栏中用localhost:8080/myapp/index-definition 访问的.

访问的方法是是struts-config.xml中设置<action-mapping>属性进行访问:

<action-mappings>
    
<action path="/index"  forward="index-definition"/>
</action-mappings>

此时即可用http://localhost:8080/myapp/index.do 进行访问.

第二种方法其实和第一种方法大同小异.思想是在每一个.jsp页面中布局装订各个元素.例如:
index.jsp

<%@ page contentType="text/html;charset=GB2312"%>
<%@ taglib divfix="html" uri="/WEB-INF/struts-html.tld"%>
<%@ taglib divfix="bean" uri="/WEB-INF/struts-bean.tld"%>
<%@ taglib divfix="tiles" uri="/WEB-INF/struts-tiles.tld"%>

<tiles:insert page="/tile/layout.jsp" flush="true">
    
<tiles:put name="header" value="/tile/header.jsp"/>
    
<tiles:put name="navigation" value="/tile/navigation.jsp"/>
    
<tiles:put name="product" value="/tile/adminlogin.jsp"/>
    
<tiles:put name="page" value="/tile/page.jsp" />
    
<tiles:put name="footer" value="/tile/footer.jsp"/>
</tiles:insert>

它将用到layout.jsp这个文件:

<TABLE width="100%"border="0" align="center" cellpadding="0" cellspacing="0" >
            
<TR>
                
<TD colspan="3" align="center">
                    
<tiles:insert attribute="header" />
                
</TD>
            
</TR>
            
<TR>
                
<TD colspan="3" align="right">
                    
<tiles:insert attribute="navigation" />
                
</TD>
            
</TR>
            
<TR height="100%">
                
<TD width="1%" height="1"></TD>
                
<TD width="99%" align="center">
                    
<tiles:insert attribute="product" />
                
</TD>
                
<TD width="1%"></TD>
            
</TR>
            
<TR>
                
<TD width="1%" height="1"></TD>
                
<TD width="99%" align="right">
                    
<tiles:insert attribute="page" />
                
</TD>
                
<TD width="1%"></TD>
            
</TR>
            
<TR>
                
<TD colspan="3" align="center">
                    
<tiles:insert attribute="footer" />
                
</TD>
            
</TR>
 
</TABLE>

当然这时候就可以直接在地址栏中用http://localhost:8080/myapp/index.jsp 访问了.使用tiles的好处就是可以灵活的组装网页的各个模块.是实现复用的好工具.

二.struts中使用 DynaActionForm

使用表单是网页里面经常遇到的事情.利用struts的标签可以很好的完成这一功能.在struts中表单分为两种.一种是ActionForm..另外一种是DynaActionForm.所谓动态是在使用表单时不需要创建对就的bean文件.ActionForm很简单,在此只说说DynaActionForm.因为在这个小项目中在很多的用户输入.数据验证部分我放到了Action中进行.于是在表示层Form中只是接收数据.用DynaActionForm实现是个很好的选择.

index.jsp

<TABLE border="0" width="100%" height="40px">
                
<TR>
                    
<TD >
                        
<html:form action="/selectAction" method="POST">    
                                    选择文法:
                                    
<html:select property="select_grammar" >
                                        
<html:options collection="grammarCollection"
                                                         property
="label"
                                                         labelProperty
="label"/>
                                    
</html:select>
                                    
<p>
                                    输入文法:                  
                                    
<logic:present name="SELECTED_GRAMMAR" scope="request">
                                    
<span id="thegrammar">         
                                        
<html:textarea   cols="40"value="${SELECTED_GRAMMAR}">
                                        
</html:textarea>*     
                                    
</span>      
                                    
</logic:present>
                                    
<p>
                                
<html:checkbox property="first" value="true" />:求first集合 <br>
                                
<html:checkbox property="follow" value="true" />:求follow集合<br>
                                
<html:checkbox property="select" value="true" />:求select集合 <br>
                                
<html:submit/>
                                
<html:reset/>
                                
</html:form>                    
                    
</TD>
                
</TR>
</TABLE>

这个页面中和使用静态表单没什么区别,只是在struts-config.xm配置稍微有些不同.

<form-bean name="selectForm" type="org.apache.struts.action.DynaActionForm">
      
<form-property name="select_grammar" type="java.lang.String" />
      
<form-property name="selected_grammar" type="java.lang.String" />
      
<form-property name="first" type="java.lang.Boolean" />
      
<form-property name="select" type="java.lang.Boolean"/>  
      
<form-property name="follow" type="java.lang.Boolean"/>       
</form-bean>

对应此表单的Action的配置:

<action path="/selectAction"
                 type
="ysu.cs.icai.action.selectAction" //定义的处理提交的类
                 input
="/index.jsp"
                 name
="selectForm"
                 validate
="false"
                 scope
="request">
                 
<forward name="SUCCESS_PATH" path="/selectindex.do"/>
</action>

和ActionForm的不同之处还在于在Action中获取提交的参数时要将ActionForm强制转换为DynaActionForm:

DynaActionForm analyzeForm = (DynaActionForm) form;

如何获取各参数如下:

import org.apache.struts.action.DynaActionForm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
/**
     * Method execute
     * 
@param mapping
     * 
@param form
     * 
@param request
     * 
@param response
     * 
@return ActionForward
     */

public class selectAction extends Action {

public ActionForward execute(
        ActionMapping mapping,
        ActionForm form,
        HttpServletRequest request,
    HttpServletResponse response) 
{
        DynaActionForm analyzeForm = (DynaActionForm) form;//强制
        String grammar=(String)analyzeForm.get("selected_grammar");    
        Boolean first=(Boolean)analyzeForm.get("first");
//
        Boolean follow=(Boolean)analyzeForm.get("follow");//
        Boolean select=(Boolean)analyzeForm.get("select");//获取提交的各个参数.
  //......something about what  you what todo.........
    return mapping.findForward("SUCCESS_PATH");
}

}

动态表单有很多优点.比如可以实现跨页的表单提交.还过也有缺点比如此时它就不能将validate框架集成到里面.有个方法就是可心自己继承DynaActionForm类在其中添加validate方法.再将实际的应用中的Form继承此类.不过此时的将失去使用DynaActionForm的灵活性.只好自己权衡了.

三.struts中使用监听器类

监听器类顾名思义就是.某些事件的触发下能执行的动作.在servlet 中有这样的类专门监听网页中的动作.整理好下:

监听器种类
监听接口 监听对象 实现方法
ServletContextAttributeListener 监听ServletContext的属性的操作 比如增加、删除、修改属性。
 
ServletContextListener 监听ServletContext 当创建ServletContext时,激发contextInitialized(ServletContextEvent sce)方法;

当销毁ServletContext时,激发contextDestroyed(ServletContextEvent sce)方法。
 

HttpSessionListener 监听HttpSession的操作 当创建一个Session时,激发session Created(HttpSessionEvent se)方法;

当销毁一个Session时,激发sessionDestroyed (HttpSessionEvent se)方法。
 

HttpSessionAttributeListener 监听HttpSession中的属性的操作 当在Session增加一个属性时,激发attributeAdded(HttpSessionBindingEvent se) 方法;

当在Session删除一个属性时,激发attributeRemoved(HttpSessionBindingEvent se)方法;

当在Session属性被重新设置时,激发attributeReplaced(HttpSessionBindingEvent se) 方法。

 

  当然还有关于request对象操作的监听器类.在此不在敖述;下面举一个我使用的例子:使用监听器实现对数据库的访问,当应用启动时即访问数据库加载信息,监听器类如下:

package ysu.cs.icai.listener;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Vector;
import javax.servlet.*;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

import org.apache.struts.util.LabelValueBean;
import ysu.cs.icai.database.ConDatabase;
/**
 * 监听器类设置数据源.在初始化web.xml文件时加载;
 */

public class ResourceInitListener implements ServletContextListener{
    
public void contextInitialized(ServletContextEvent init) {
        
        ServletContext application = init.getServletContext();        
        String driverClass = application.getInitParameter("driverClass");
        String jdbcURL = application.getInitParameter("jdbcURL");
//获取web.xml中配置的参数.
        Vector grammarCollection=new Vector();
        java.sql.Connection myConnection=
null;
        java.sql.Statement st;
        ResultSet set;
        String sql1="select * from GRAMMAR";    
        ConDatabase dataSource = 
new ConDatabase();
        
try {        
            dataSource.setDriver(driverClass, jdbcURL);
            application.setAttribute("DATASOURCE",dataSource);
            
//System.out.print("ATOM:数据初始化成功!");            
        }
 catch(ClassNotFoundException e) {
            e.printStackTrace();
        }

        
try{
            myConnection=dataSource.getConnection();
            st=myConnection.createStatement();
            st.execute("use icaidb;");
            System.out.print("ATOM:change to use icaidb!");
            set=st.executeQuery(sql1);
            
while(set.next()){
                grammarCollection.add(
new LabelValueBean(set.getString(1),set.getString(2)));
            }

    
//System.out.print("ATOM:数据检索成功!");
        }
catch(SQLException er){
            er.printStackTrace();
            
//System.out.print("ATOM:数据库连接异常!");
        }

        
if(grammarCollection!=null){
            application.setAttribute("grammarCollection",grammarCollection);
        }

    }
    
    
public void contextDestroyed(ServletContextEvent destroy) {        
        ServletContext application = destroy.getServletContext();

    }

}

为了使用启动应用时能调用到这个文件还必须在web.xml为监听器进行配置.
 

 <context-param>
  <param-name>driverClass</param-name>
  <param-value>com.microsoft.jdbc.sqlserver.SQLServerDriver</param-value>
</context-param>
<context-param>
  <param-name>jdbcURL</param-name>
  <param-value>jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=icaidb;user=admin;password=admin</param-value>
<!--jdbcURLl和driverClass是为ysu.cs.icai.listener.ResourceInitListener配置的两个参数-->
</context-param>
<listener>
  <listener-class>ysu.cs.icai.listener.ResourceInitListener</listener-class>
</listener>

这个文件启动后ServletContext中的参数grammarCollection会被保存到ServletContext域中.在jsp或action文件中可使用getServletContext().getAttribute("grammarCollection")访问这个对象.

<html:select property="select_grammar">
<html:options collection="grammarCollection"                   
        property="laber"                                
        labelProperty="value"/>
</html:select>

 以上代码可以生成一个从数据库动态检索列表项的下拉列表.因为grammarCollection是一个Vector类型的对象.可以这样为其添加值对:

grammarCollection.add(new LabelValueBean("laber1","value1");

关于lisener的东西还很多.偶只是用到这么多,欢迎留言交流.

四.struts中Ajax 的简单应用

Ajax 现在已经热火朝天了,正好做这个小项目时有个功能最理想的方法就是用Ajax时,因为没学过javascript,费了好大的劲,也请教了不少人才弄明天下面这段代码是怎么回事.功能是实现了,不过对Ajax的实在了解不多,有空了以后好好再琢磨.不懂javascript.不会Ajax.struts中Ajax的简单应用.可以完全不用理会javascript.现在就来看看它的入门级用法.看看我是怎么实现的.

用到的Ajax代码如下:(可以不用知道是怎么回事,不过介意看完整个代码)

/**
 * Ajax.js
 *
 * Collection of Scripts to allow in page communication from browser to (struts) server
 * ie can reload part instead of full page
 *
 * How to use
 * ==========
 * 1) Call retrieveURL from the relevant event on the HTML page (e.g. onclick)
 * 2) Pass the url to contact (e.g. Struts Action) and the name of the HTML form to post
 * 3) When the server responds ...
 *         - the script loops through the response , looking for <span id="name">newContent</span>
 *          - each <span> tag in the *existing* document will be replaced with newContent
 *
 * NOTE: <span id="name"> is case sensitive. Name *must* follow the first quote mark and end in a quote
 *         Everything after the first '>' mark until </span> is considered content.
 *         Empty Sections should be in the format <span id="name"></span>
 */


//global variables
  var req;
  
var which;


  
/**
   * Get the contents of the URL via an Ajax call
   * url - to get content from (e.g. /struts-ajax/sampleajax.do?ask=COMMAND_NAME_1) 
   * nodeToOverWrite - when callback is made
   * nameOfFormToPost - which form values will be posted up to the server as part 
   *                    of the request (can be null)
   */

  
function retrieveURL(url,nameOfFormToPost) {
    
    
//get the (form based) params to push up as part of the get request
    url=url+getFormAsString(nameOfFormToPost);
    
    
//Do the Ajax call
    if (window.XMLHttpRequest) // Non-IE browsers
      req = new XMLHttpRequest();
      req.onreadystatechange = processStateChange;
      
try {
          req.open("GET", url, 
true); //was get
      }
 catch (e) {
        alert("Problem Communicating with Server "+e);
      }

      req.send(
null);
    }
 else if (window.ActiveXObject) // IE
      
      req = 
new ActiveXObject("Microsoft.XMLHTTP");
      
if (req) {
        req.onreadystatechange = processStateChange;
        req.open("GET", url, 
true);
        req.send();
      }

    }

  }


/*
   * Set as the callback method for when XmlHttpRequest State Changes 
   * used by retrieveUrl
  */

  
function processStateChange() {
  
        
if (req.readyState == 4) // Complete
      if (req.status == 200) // OK response
        
        
//alert("Ajax response:"+req.responseText);
        
        
//Split the text response into Span elements
        spanElements = splitTextIntoSpan(req.responseText);
        
        
//Use these span elements to update the page
        replaceExistingWithNewHtml(spanElements);
        
      }
 else {
          alert("Ajax response:"+req.responseText);
        
//alert("Problem with server response:  " + req.statusText);
      }

    }

  }

 
 
/**
  * gets the contents of the form as a URL encoded String
  * suitable for appending to a url
  * @param formName to encode
  * @return string with encoded form values , beings with &
  */
 
 
function getFormAsString(formName){
     
     
//Setup the return String
     returnString ="";
     
      
//Get the form values
     formElements=document.forms[formName].elements;
     
     
//loop through the array , building up the url
     //in the form /strutsaction.do&name=value
     
     
for ( var i=formElements.length-1; i>=0; --i ){
         
//we escape (encode) each value
         returnString=returnString+"&"+escape(formElements[i].name)+"="+escape(formElements[i].value);
     }

     
     
//return the values
     return returnString; 
 }

 
 
/**
 * Splits the text into <span> elements
 * @param the text to be parsed
 * @return array of <span> elements - this array can contain nulls
 */

 
function splitTextIntoSpan(textToSplit){
 
      
//Split the document
     returnElements=textToSplit.split("</span>")
     
     
//Process each of the elements     
     for ( var i=returnElements.length-1; i>=0; --i ){
         
         
//Remove everything before the 1st span
         spanPos = returnElements[i].indexOf("<span");        
         
         
//if we find a match , take out everything before the span
         if(spanPos>0){
             subString=returnElements[i].substring(spanPos);
             returnElements[i]=subString;
         
         }
 
     }

     
     
return returnElements;
 }

 
 
/*
  * Replace html elements in the existing (ie viewable document)
  * with new elements (from the ajax requested document)
  * WHERE they have the same name AND are <span> elements
  * @param newTextElements (output of splitTextIntoSpan)
  *                    in the format <span id=name>texttoupdate
  */

 
function replaceExistingWithNewHtml(newTextElements){
 
     
//loop through newTextElements
     for ( var i=newTextElements.length-1; i>=0; --i ){
  
         
//check that this begins with <span
         if(newTextElements[i].indexOf("<span")>-1){
             
             
//get the name - between the 1st and 2nd quote mark
             startNamePos=newTextElements[i].indexOf('"')+1;
             endNamePos=newTextElements[i].indexOf('"',startNamePos);
             name=newTextElements[i].substring(startNamePos,endNamePos);
             
             
//get the content - everything after the first > mark
             startContentPos=newTextElements[i].indexOf('>')+1;
             content=newTextElements[i].substring(startContentPos);
             
             
//Now update the existing Document with this element
             
                 
//check that this element exists in the document
                 if(document.getElementById(name)){
                 
                     
//alert("Replacing Element:"+name);
                     document.getElementById(name).innerHTML = content;
                 }
 else {
                     alert("Element:"+name+"not found in existing document");
                 }

         }

     }

 }

视图层的jsp页面如下:(仅贴出使用相关的代码)

<%@page contentType="text/html;charset=gb2312" language="java" isELIgnored="false"%>
<html:form action="/lr0Analyze" method="POST"  >
<align="left">
选择文法:
  
<html:select property="select_grammar" onchange="retrieveURL('grammarAjax.do?tableName=GRAMMAR','lr0AnalyzeForm');">
     
<html:options collection="grammarCollection" property="label" labelProperty="label"/>
  
</html:select>
</P>
  
<align="left">
  输入文法:                                                                      
  
<!-- 1.Default Return Blank -->  
  
<logic:notPresent name="SELECTED_GRAMMAR" scope="request">
  
<span id="thegrammar">                                       
    
<html:textarea property="selected_grammar" rows="7" cols="40">
    
</html:textarea>*
  
</span>
  
</logic:notPresent>
  
<!-- 2.Return content if requested-->    
  
<logic:present name="SELECTED_GRAMMAR" scope="request">
    
<span id="thegrammar">         
      
<html:textarea property="selected_grammar" rows="7" cols="40" value="${SELECTED_GRAMMAR}">
      
</html:textarea>*     
    
</span>      
  
</logic:present>                                           
  
</P>
  
<align="left">
  输 入 串  :
  
<html:text maxlength="40" property="input_lr0" size="40"/>必须以<FONT color="#ff0000"></FONT>号结尾
</P>
  
<align="left">
  
<html:submit/><html:reset/>
</P>
</html:form>

如代码所示:代码中有两个<span id="thegrammar"> (thegrammar是保存在request域内的一个参数)两个代码表示的意思分别是:1.当request域内不存在thegrammar时使用期1。2.当request域内存在thegrammar时使用期2。form提交的目标是grammarAjax.do.(struts-config.xml中定义的名字)

<html:textarea property="selected_grammar" rows="7" cols="40" value="${SELECTED_GRAMMAR}">

将<html:textarea>的值设为${SELECTED_GRAMMAR};

Ajax可以接收任一后台处理的结果。jsp,servlet,action等。要做的只要将处理结果保存到相应的区域(request,reponse,application)使用action处理如下(此即为grammarAjax.do 对应的代码):

package ysu.cs.icai.action;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import java.sql.*;
import ysu.cs.icai.common.*;
import ysu.cs.icai.database.ConDatabase;

/** 
 * MyEclipse Struts
 * Creation date: 08-30-2006
 * 
 * XDoclet definition:
 * @struts.action validate="true"
 */

public class GrammarAjaxAction extends Action {
    
/** 
     * Method execute
     * 
@param mapping
     * 
@param form
     * 
@param request
     * 
@param response
     * 
@return ActionForward
     */

    
public ActionForward execute(
        ActionMapping mapping,
        ActionForm form,
        HttpServletRequest request,
        HttpServletResponse response) 
{
        
        ConDatabase dataSource;
        java.sql.Connection myConnection;
        Statement st;
        ResultSet set;
        String sel=request.getParameter("select_grammar");
        String labeltable=request.getParameter("tableName");
        String sql="select * from "+labeltable+" where label='"+sel+"'";
//the value of "tableName" is GRAMMAR OR FORMULA 
        
        
try{
            ServletContext application=request.getSession().getServletContext();
            
            dataSource=(ConDatabase)application.getAttribute("DATASOURCE");
            myConnection=dataSource.getConnection();

            st=myConnection.createStatement();
            st.execute("use icaidb;");
            set=st.executeQuery(sql);
            
while(set.next()){
                String re=set.getString(2);
                re=Common.removeTransferredMeaning(re);
                //System.out.print(re);
                request.setAttribute("SELECTED_GRAMMAR",re);        
                
//System.out.print(re);
            }

        }
catch(SQLException er){
            er.printStackTrace();
        }

        
return mapping.findForward("SUCCESS_PATH");
    }

}

这段代码相信大家都看能看明白:根据select_grammar和tablename这两个属性在一个数据库中检索相应的数据后,并将结果返回.(注意相保存在到request,reponse,application...任一作用域中).

 可以参考如下文章(以下列表摘自www.ibm.com):

 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号