说来可笑,接到这个小项目的时候还不知道什么是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" >
<P 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>
<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>
<P align="left">
输 入 串 :
<html:text maxlength="40" property="input_lr0" size="40"/>必须以<FONT color="#ff0000">#</FONT>号结尾
</P>
<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):