[Struts]在JSP里处理比较复杂的内容?

今天遇到一个问题,到现在也没能比较圆满的解决,是不是Struts在标签库上还不够完善呢。比如有一个界面是显示课件列表的,在最后一栏里可以对数据进行操作,如下所示:

Code Name Author OP
10000001 风洞模型课件 刘金东 View Edit Delete
10000002 卡门涡阶课件 季铭义 View Edit Delete
10000003 复变函数课件 秦江 View Edit Delete
10000004 听力课件 郭长凯 View Edit Delete

现在希望当用户按删除时先弹出个确认框,提示“是否确认删除风洞模型课件”,用户可以选择确认或取消。其中“是否确认删除”是在资源文件里定义的(prompt.confirm.delete=是否确认删除{0}),“风洞模型课件”是课件的名称,课件bean名为"ware"。如果写成HTML,就是:

<a href="" onclick="return confirm('是否确认删除风洞模型课件')">Delete</a>

但因为信息都是动态的,所以就有问题了。因为在<html:link>的onclick="..."里,"<%"必须紧跟在第一个单引号后才能正确解析,即不能写为onclick="return confirm('<%=str%>')"。所以,现在有两种方法实现所需要的功能:

1、不用<html:link>,直接用HTML的<a>标记:

<bean:define id="toDel" name="ware" property="name" type="String"/>
<a href="delete.do?code=<bean:write name="ware" property="code"/>" 
    onclick="return confirm('<bean:message key="prompt.confirm.delete" bundle="root" arg0="<%=toDel%>"/>');">
    <bean:message key="course.list.op.delete"/>
</a>

2、使用<html:link>,事先定义一个只含一个参数的script函数,代码如下:

<script language="JavaScript">
<!--
function confirmDelete(str){
    return confirm('<bean:message key="prompt.confirm.delete" bundle="root" arg0="'+str+'"/>');
  }
-->
</script>

然后在删除链接的地方这样写:

<bean:define id="toDel" name="ware" property="name" type="String"/>
<html:link action="/delete" paramId="code" paramName="ware" paramProperty="code" onclick="<%="return confirmDelete('"+toDel+"');"%>">
    <bean:message key="course.list.op.delete"/>
</html:link>

这两种方法都能达到目的,我暂时使用了第2种用法,毕竟在struts程序的jsp里直接使用<a>标记有点别扭。我看了一下struts文档,能把资源中的{0}转换为实际内容的标签好象只有<bean:message>这一个,其实如果有办法让<bean:message>得到的内容放进某个bean里就很好办了,可惜……。

另外,没研究过EL标签库,不知道会不会有帮助。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/08/26/36764.html

[Struts]使用tiles管理界面遇到困难

上个周末都在研究怎么用tiles管理示范中心项目的界面,没想到遇到了不少麻烦,到现在也没解决。首先,示范中心项目有很多个模块,我们是用struts的模块功能分开的。本来想的是在缺省模块里定义几个公用的界面定义(definition),然后再各模块里都继承这个定义,并修改必要的tile就可以了。没想到不管怎么设置,模块里的定义都继承不到缺省的定义。缺省模块里:

<plug-in className="org.apache.struts.tiles.TilesPlugin" >
  <set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml" />
</plug-in>

教师模块里:

<plug-in className="org.apache.struts.tiles.TilesPlugin" >
  <set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml,/WEB-INF/tiles-defs-teacher.xml" />
</plug-in>

tiles-defs.xml里:

<definition name="classicLayout" path="/layout/classic.jsp">
    <put name="header" value="/header.jsp" />
    <put name="menu" value="/teacher/list.do"/>
    <put name="main" value=""/>
    <put name="footer" value="/footer.jsp" /> 
</definition>

tiles-defs-teacher.xml里:

<definition name="listLayout" extends="classicLayout">
    <put name="main" value="/teacher/list.jsp"/>
</definition>

然后在教师模块里forward到listLayout,提示path没有以"/"开头,就是没有找到listLayout这个定义了。我试了很多写法,包括设置moduleAware的属性,都没有成功。

后来想就在每个模块里都写classicLayout的定义吧,都指向同一个.jsp定义文件就可以了。又遇到新问题,我想在teacher模块里显示menu模块里的内容,会提示找不到所需资源,因为我是在teacher模块里,menu模块的资源是无法访的,除非在menu模块的配置文件里指定key,再在.jsp文件里强制指定bundle的名称,我觉得这个方法太不雅了,同时要做不少修改。

<definition name="classicLayout" path="/layout/classic.jsp">
    <put name="header" value="/header.jsp" />
    <put name="menu" value="/menu/list.jsp"/>
    <put name="main" value="/teacher/list.jsp"/>
    <put name="footer" value="/footer.jsp" /> 
</definition>

还有,<put>里的value只能是.jsp吗,用.do行不行,我试的结果是不行,虽然没报任何错误,但页面生成到那之前就截止了。郁闷!

[Struts]处理表单中值为空的日期类型字段

在示范中心项目中,我们把ActionForm中日期类型的字段指定为String类型,而在对应的JavaBean中指定为java.sql.Date类型。当用户提交表单的时候,在Action里使用BeanUtils.copyProperties()方法从ActionForm构造JavaBean对象(详见利用BeanUtils在对象间复制属性)。这个方法在大部分时候都很好,但有一个问题,就是当用户没有填写日期类型字段时(而该字段并非必填),validator不会提出警告,而在copyProperties()时会报类型转换异常,原因是这时ActionForm中的该字段的值是空字符串(""),负责字符串向Date转换的SqlDateConverter类调用Date.valueOf("")方法,显然""是无法转换为日期的,所以会抛出异常。

通过查看代码和资料,我发现这个问题的解决方法其实非常简单。只要把带缺省值参数的SqlDateConverter重新注册一下,覆盖原有的注册信息就可以了,这个注册语句一般是写在系统初试化的地方,对于Struts应用程序,当然做在PlugIn里最方便。代码如下:

package com.acme;

import javax.servlet.ServletException;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.converters.SqlDateConverter;
import org.apache.struts.action.ActionServlet;
import org.apache.struts.action.PlugIn;
import org.apache.struts.config.ModuleConfig;

public class ConverterPlugIn implements PlugIn{

    public void init(ActionServlet servlet, ModuleConfig config) throws ServletException {
        ConvertUtils.register(new SqlDateConverter(null),java.sql.Date.class);
    }

    public void destroy() {
        ConvertUtils.deregister();
    }
}

注意SqlDateConverter的构造方法是带有参数null的,这表示遇到不能解析的字符串就返回空值。而deregister()方法的作用是恢复ConvertUtils的缺省注册表。为了使这个PlugIn起作用,要在struts-config.xml里增加一句话:

<plug-in className="etc.ConverterPlugIn" />

日期字段往往会给我们的开发带来麻烦,其实在Struts应用程序里,只要把这些转换类搞熟了,总可以找到很方便的办法。常见的问题还有如何指定日期输入格式,怎样处理java.util.Date的转换,等等,在这个链接里有解决这些问题的方法,道理都是一样的。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/08/17/34106.html

[Hibernate]xDoclet生成hbm的一个bug

做示范中心项目时遇到的,类Teacher实现接口BusinessObject,在接口里用@hibernate.class,在类里用@hibernate.joined-subclass-key column="oid"和@hibernate.joined-subclass,执行ant任务时只生成了BusinessObject.hbm.xml,而且在里面没有关于Teacher的定义。为此折腾了好一阵,后在在网上找到一个贴子说的是同一个问题,还提供了一个patch,不过还没试好不好使,内容如下。(快该回家了,晚上继续写)

diff -u -1 -b -p -r1.20 HibernateTagsHandler.java
--- HibernateTagsHandler.java   14 Jun 2003 13:58:10 -0000      1.20
+++ HibernateTagsHandler.java   3 Nov 2003 00:58:27 -0000
@@ -285,3 +285,7 @@ public class HibernateTagsHandler
                 }
-                else if (clazz.getSuperclass() != null && clazz.getSuperclass().getQualifiedName().equals(typeName)) {
+                else if ((clazz.getSuperclass() != null &&
+                    clazz.getSuperclass().getQualifiedName().equals(typeName))
+                    ||
+                    (getCurrentClass().isInterface() &&
+                    clazz.isImplementingInterface(typeName))) {
                     log.debug("is a subclass");

现在决定不用这个patch的方法了,改源码得重新build,而且以后就不能用通用包了。暂时拿抽象类代替接口吧,差不多。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/08/13/33157.html

[Struts]让Dreamweaver显示Struts标签的插件

Dreamweaver(简称DW)的设计视图里不能显示struts标签,只能手动改代码。为此我找了好久,终于还是在DW网站上找到了,只有8K大,虽然没有漂亮的图标,但显示的信息还是很够用的。现在总算可以用DW编辑含有struts标签的jsp文件了!

file
图1 在DW里显示struts标签

这个文件我已经放在FTP上了,请点这里下载。如果连不上,请用这个地址。下载以后直接双击就安装,然后重开DW就行了。不需要了可以在Extension Manager里卸载。

Update: 感谢dudu帮我开通了文件上传功能,本地下载

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/08/07/31028.html

[翻译]JavaBean组件与JSP技术结合

Web架构师Brett McLaughlin向我们展示了怎样利用JavaBean和JSP技术在Web页面中保存和传递数据,以及如何设计可以得到更大的灵活性。

到目前为止,在JSP最佳实践系列里,我们已经讨论了相当一部分基础知识。在前面的两个章节里,你应该学会了如何使用JSP的include机制将网站以外的内容包含在页面或应用程序里。共有两种不同类型的include:静态的include命令和动态的jsp:include标记。

同时,我们还没有涉及到在父页面(在我们的例子中是网站的首页)和被包含内容之间的通信问题。而实际上这种情况是非常普遍的。当你开始建立一个真正的网站或者web应用程序时,通常你会需要这样的通讯机制,举个例子,你的网站可能会在首页产生一些标题一类的小段文字,这些文字要出现在页眉或页脚页面中。在这一部分里,你将学到如何在页面间传递数据,以及如何在被包含的页面中使用这些数据。

注:这部分的全部例子都基于JavaServer Pages技术,为了能运行它们,你需要建立一个JSP兼容的web容器,在你本地计算机或某个用于测试的服务器上都可以。同时,你还要有一个文本编辑器或者集成环境来编写你的JSP页面。

用于保存数据的JavaBean

让我们假想这样的一个网站,在这个网站里每个页面有一小句“口号”(例如“书:装满知识的容器”或者“唱片:值得一听”)和一个标题。父页面(有时也被称作主页面)决定了每一页的口号,但真正产生html输出这个口号的是页眉页面,它是被包含的。要实现这个目的,父页面必须能够把口号传递给页眉页面,页眉页面得到这个口号,将它以页面标题的形式输出。

首先我们需要某种对象来保存被传递的数据,恰好(这并非偶然)JavaBean组件就是这样一个合适的选择,它与JSP技术配合得天衣无缝,简单的使用取值方法和赋值方法就可以控制你要的数据。稍有java编程经验的读者可能已经想到,get()就是一个取值方法,因为它读取数据;而set()是一个赋值方法,因为它改变数据。
列表1展示了我们所需要的一个JavaBean的代码,PageHeaderinfo包含了网站页眉信息。

<![CDATA[
package com.newInstance.site.beans;
import java.io.Serializable;
public class PageHeaderInfo implements Serializable {
     /** The title of the page */
     private String pageTitle;
     /** The slogan of the page */
     private String pageSlogan;
     public String getPageTitle() {
       return pageTitle;
     }
     public void setPageTitle(String pageTitle) {
       this.pageTitle = pageTitle;
     }
     public String getPageSlogan() {
       return pageSlogan;
     }
     public void setPageSlogan(String pageSlogan) {
       this.pageSlogan = pageSlogan;
     }
}
]]>

作为第一个练习,将这个文件保存为PageHeaderInfo.java并编译它。接下来,把得到的class文件PageHeaderInfo.class放在你的web应用程序的WEB-INF/classes目录下。请确保目录包含了包的结构,例如:

$<TOMCAT-ROOT>/webapps/$<WEB-APP-NAME>/WEB-INF/classes/com/newInstance/  site/beans/PageHeaderInfo.class

你可以使用类似这样的路径安排web应用程序中用到的类。按以上步骤做到这里,下面就可以向PageHeaderInfo里存入数据然后在不同的JSP页面中获取了。

传递已保存的数据

在我们的网站里,页眉页面包含向不同页面传递不同口号的代码。查看前面的章节就会发现,页眉页面(header.jsp)是通过jsp:include标记被包含的文件。列表2展示了网站首页如何通过标记把数据传递给header.jsp文件。

<![CDATA[
<%@ page language="java" contentType="text/html" %>
<html>
<head>
     <title>newInstance.com</title>
     <meta http-equiv="Content-Type" 
       content="text/html; charset=iso-8859-1" />
     <link href="/styles/default.css" rel="stylesheet" type="text/css" />
</head>

<body>
<jsp:include page="header.jsp" flush="true">
     <jsp:param name="pageTitle" value="newInstance.com"/>
     <jsp:param name="pageSlogan" 
       value="Java and XML :: Turning theory into practice" />
</jsp:include>
<%@ include file="/navigation.jsp" %>
<jsp:include page="bookshelf.jsp" flush="true" />

<jsp:include page="/mt-blogs/index.jsp" flush="true" />

<%@ include file="/footer.jsp" %>
</body>
</html>
]]>

可以看出,标题是被传递过去作为口号的。

你可能已经注意到了,在你建立页面的时候,不一定需要JavaBean组件实际存在。我总是先写好JavaBean,有一个很好的理由:JSP参数名必须与JavaBean属性名匹配,先完成JavaBean可以保证你在编写JSP页面时使用合适的参数名称。

获得数据

当你完成了JSP参数和JavaBean属性的编码,一旦数据被传递给header.jsp,这个页面就可以开始获取数据了。列表3展示了header.jsp页面。它的大部分内容是html,请注意里面的JSP脚本,在你研究过这些代码后我会在后面向你解释。

<![CDATA[
<!-- Begin header section -->
<%@ page language="java" contentType="text/html" %>
<jsp:useBean id="pageHeaderInfo"
class="com.newInstance.site.beans.PageHeaderInfo">
     <jsp:setProperty name="pageHeaderInfo" property="*" />
</jsp:useBean>

<table width="100%" border="0" cellspacing="0" cellpadding="0">
     <tr>
       <td width="91" height="50" align="right" valign="top"
           bgcolor="#330066"><font color="#FFFFFF"><img
           src="/images/header-lions.gif" 
           width="90" height="60"></font></td>
       <td colspan="3" align="left" valign="top"
           bgcolor="#000000"><table width="100%" height="60" border="0"
           cellpadding="0" cellspacing="0">
           <tr>
             <td width="261" rowspan="2"><img
               src="/images/header-title.gif" width="261" height="60"></td>
             <td class="pagetitle" width="249" height="55" align="right"
               valign="bottom"><jsp:getProperty name="pageHeaderInfo"
               property="pageSlogan"/></td>
             <td width="10" height="55"> </td>
           </tr>
           <tr>
             <td height="5"><img src="/images/spacer.gif" width="1"
               height="5"></td>
             <td height="5"><img src="/images/spacer.gif" width="1"
               height="5"></td>
           </tr>
         </table></td>
       <td width="141" bgcolor="#000000">
         <font color="#FFFFFF"> </font>
       </td>
     </tr>
<!-- End header section -->
]]>

第一行代码标识了该页面为一个JSP页面,然后通过jsp:useBean标记声明需要访问PageHeaderInfo这个JavaBean,id属性为这个bean指定了一个名称,通过该名称可以在JSP页面中使用bean;class属性指定了JavaBean类的全名。相邻的jsp:setProperty标记说明了JavaBean(通过id属性标识)的所有属性都以请求数据赋值,也就是说,为这个bean里的每个属性(例如pagetitle和pageslogan)寻找名称对应的请求参数来赋值。这些请求参数可以来自客户端的浏览器,也可以来自包含这一页的其他JSP页面。在这种情况下,唯一的请求数据是由父页面创建的。对于我们的网站,首页(index.jsp)发送pagetitle和pageslogan,其值分别为“newinstance.com”和“Java and XML: Turning theory into practice”。

一旦bean的属性被赋值后,页面就可以使用这些数据了。对于header.jsp,在后面的代码里可以看到,通过jsp:getProperty标记使用了这些数据。Jsp:getProperty标记通过name参数标识从哪个对象取数据,通过property参数标识取对象的哪一个属性。取得的数据值自动插入到页面的输出,也就是输出到父页面里,从而得到一个无缝的、动态的页面口号,在JSP页面间传递数据就是这么简单!你可以为一个JSP页面增加任意多的bean,每一个bean都可以有任意多的属性,足以应付任何复杂的请求数据。

处理需求变化

改变是软件开发者最大的烦恼,你写好了你的bean,在JSP页面里也写好了使用它们的代码,这时web应用程序的需求似乎不可避免的会发生变化。如果这个改变要求更多的属性(大多数情况都是如此),你不得不改写你的JavaBean源代码,重新编译它,还要确定JSP页面能够正确访问新生成的bean类。有些情况下,你可以不必对bean进行处理,如果保存或获取属性页面不再使用了(也就是说,即使那个页面还在站点的目录里,但你不再使用该页面),这时你可以不用去管原来的代码即可。实际上,我就遇到过这种情况!

我的个人网站的页眉页面header.jsp用于生成html的头部,以前有一段时间里,这一页向我的其他页面头部里的title标记里插入一个标题,后来我做了修改,在header.jsp里不再使用页面标题了。但我并没有从PageHeaderInfo里把pageTitle移除;实际上,我甚至在大部分JSP页面里连jsp:param标记都没有移除,这个标记的作用本来是为页面设置标题的。我认为花这些工夫不值得,因为我确信保留这些数据不会带来任何坏的影响(也许某一天我还会重新用到呢!)。因此,如果你遇到同样的情况,不用浪费时间了--有处理这些琐事的时间不如用来为你的web应用程序增加些新的、有趣的、实用的功能。

下一次

当你熟练掌握了在JSP页面间传递数据的方法后,试着自己写一些有用的JavaBean并且看看能不能把它们用在你的站点里。通过和这些bean的接触,还有jsp:useBeanjsp:param以及jsp:get/setProperty的使用,你应该能够做出一些很酷的功能了!在下次的最佳实践里,我将向你展示使用JSP向站点增加外部内容的方法,JSTL标记和我们熟悉的include标记差不多,它使得JSP更灵活和更高效。在这之前,请用功准备,到时再见!

英文原文

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/08/07/30950.html

Web服务系列(三) XML技术

file

对于XML这个名字,我们已经再熟悉不过了。它可以说是既简单又复杂,因为XML本身具有简单明确的规则,但随着越来越多基于XML技术的新应用的出现,它又演化出各种复杂的语言。希望这篇帖子能为你解决以下问题:XML是什么、我们为什么需要它以及怎样使用它。

XML,全称是可扩展标记语言(eXtensible Markup Language),它是一种标记语言。标记语言的特点是具有三个要素:标记、元素和属性。先看一段简单的XML例子,如下所示:

<?xml version="1.0"?>
<team>
  <member leader="true">
    <name>张浩</name>
    <age>25</age>
  </member>
  <member leader="false">
    <name>孙亮</name>
    <age>25</age>
  </member>
</team>

可以看出XML文档具有树状的结构,应该说这种结构是很适合描述数据的,所以会有不少人把XML作为持久化数据的方式。上面的XML文档中,用尖括号括起来的是标记,例如<team><member>等等,带斜线/的是结束标记,不带的是开始标记,如果是空标记可以写作<team />的形式;开始标记和结束标记与它们之间的内容合称元素;属性是一个名称-值对,例如leader="true"表示leader是member元素的属性。

从XML的格式很容易想到HTML,HTML也是一种标记语言,还有WML、XHTML等等都是标记语言,XML与它们的不同之处在于:XML是创造这些标记语言的元语言。如果拿面向对象语言来类比的话,那就是HTML、WML、XHTML等等都继承了XML,是XML的子类。因此,凡是XML具备的特性,它们也都具备。例如,一个文档只能有一个根元素,标记可以嵌套但不能交叉,属性必须用双引号括起来,开始标记和结束标记必须配套等等。如果一个XML文档不遵守这些规则,则称它是无效的。另外,还可以通过DTD或Schema对XML格式增加额外的约束,例如可以要求<team>元素下至少有一个<member>元素,<age>元素为可选的等等,如果XML文档是有效的同时还满足这些额外要求,则称这个文档是格式良好的。

DTD和Schema是验证XML是否格式良好的两种方式。DTD出现得比较早,它不是XML格式的;Schema则是XML格式的,并且功能更强,支持正则表达式。在XML文档头部可以引用这些文件,处理该XML文档时就会对它进行验证,如果验证失败则不会做进一步处理。为节约篇幅,DTD和Schema的格式这里就不细说了。一般来说,如果在程序中需要定义自己的XML格式,最好先定义DTD或Schema,我们平常使用的大部分XML文档如web.xml、struts-config.xml都有自己的DTD或Schema用来保证格式。

还要说一下名称空间的问题。名称空间是标记的前缀,XML文档在实际应用中可能会被合并,这个前缀保证了合并后的文档中不会出现冲突的标记。为了保证这个唯一性,名称空间一般使用URL的格式,例如:

<myNS:team xmlns:myNS="http://www.mysite.com">
    ...
</myNS:team>

其中myNS是我们随便起的名字,后面的xmlns:myNS属性指定了这个名字代表的名称空间,应该注意真正有意义的是team这个名字。一个完整的标记应该是名称空间:标记名这样的形式,带有名称空间的XML文档读起来会有点乱,所以要认清哪些是重要的,哪些是暂时可以忽略的。

作为一种描述数据的方式,只要发挥想象力,XML可以有无限多种用途,你订阅过RSS吗,那也是其中之一。在Web服务中,我们用XML在服务提供者和使用者之间传递请求和响应数据(SOAP是其中一种格式)、用XML描述服务(例如WSDL),还用XML将服务组装成完整的流程(比如使用BPEL4WS),这些格式规范将在后面的帖子中一一介绍。对了,是XML的可扩展性成就了它们。

要在程序中使用XML,也许是从XML格式的配置文件中读取信息,或是向其他系统提供XML格式的数据,或者其他方式,最直接的方法是使用XML解释器,目前比较常见的DOM、SAX、JDOM和JAXP,其中JAXP作为Java扩展是一个统一的接口,前三者是它的实现方式。关于这些解释器的比较,有很多文章可以参考,这里就不赘述了,我用过DOM和JDOM,比较喜欢后者,因为代码量会小一些。

虽然直接使用XML解释器处理XML格式信息并在服务提供者和使用者间传递也是Web服务,但那样太麻烦了,我们将不得不处理各种琐碎问题(例如数据类型映射),同时产生大量代码。因此,有必要使用专门处理Web服务中各种专用XML格式的解释器,Apache Axis(前身是Apache SOAP)就是其中一种,它可以解释SOAP信息,比起直接用前面所说的XML解释器方便很多。

关于XML还有太多内容,例如XSL用来表现XML、XSLT用来在不同格式XML间转换,XPATH用来在XML文档中找到合适的元素,等等。怎么样,帖子开头提到的问题解决了吗?如果没有也没关系,IBM开发人员网站上有一个XML专区,在那里你一定会大开眼界,如果你是新手,先看看这个教程,可比我写得好多了,呵呵...下一贴开始讲SOAP。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/08/06/30885.html

Web服务系列(二) Web服务的结构

我发现写日志可以帮助自己整理思路,有些技术在一段时间不用后,如果以日志的形式写出来,对于巩固记忆是十分有效的。比如这个Web服务系列,为了尽量避免错误,我会重新查阅资料,在这个过程中又能学到新的知识。不过,Web服务涉及的面太广了,而且新技术出现那么快,即使全部时间都用来研究它也不可能面面俱到,就像一本很厚很厚的书,经常翻翻反而会有意想不到的收获。

上一篇帖子里已经说过Web服务是做什么用的了,在这一篇里要说说Web服务的结构,也就是Web服务的协议栈。协议是各角色间用来沟通的基础,之所以称为栈,是由于这些协议是一层层垒起来的,下面一层是上面一层的基础。就像OSI的七层网络协议的关系。

现在要向你介绍一些概念了,它们是:XML、SOAP、WSDL、UDDI和BPEL4WS。对于XML相信大家都不会陌生,它是可扩展标记语言(eXtensible Markup Language)的缩写,是Web服务各种协议的基础;SOAP是简单对象访问协议(Simple Object Access Protocal)的缩写,它主要用于在服务提供者和使用者之间传送各种请求和应答数据;WSDL代表Web服务定义语言(Web Services Description Language),服务提供者使用这种语言发布自己的Web服务,供潜在的服务使用者使用;UDDI的意思是统一描述发现和集成(Universal Description, Discovery and Integration),UDDI项目由UDDI社区维护,服务提供者可以将自己的服务注册到UDDI服务器中,服务使用者可以在服务器中浏览和查询所需服务;BPEL4WS的意思是用于Web服务的业务流程执行语言(Business Process Execution Language for Web Services),它可以将多个Web服务组装成完整的业务流程,体现了Web服务的真正强大之处(组装)。

只这么简单一说,你可能对其中有些概念并不明白,不过没有关系,在以后的帖子里会对每个概念展开来说明。还要说明一点,列出的这些概念是实际构造和使用Web服务时使用最为广泛的技术,但Web服务并不一定必须使用这些,除XML外,其他技术都有替代品,只是并不那么流行而已。下面我们就来看看Web服务的协议栈是个什么样子的吧,如图所示。

file
图1 Web服务协议栈

最底层是服务传输层,在图中可以看到Web服务可以使用多种(OSI应用层)网络协议进行消息传递,HTTP是使用最为广泛的,因为HTTP的请求应答模式十分符合RPC类型调用,SMTP主要用于异步方式的调用,例如订阅信息等等。

服务消息层的协议定义了消息的格式,在这一层里几乎全部是以SOAP为协议的,至少我还没见过使用其他协议的例子。SOAP的基础是XML,也就是说,SOAP消息一定都是XML格式的。

服务描述层的协议用于对如何使用这个Web服务进行描述,描述信息一般包括使用到的数据类型、消息格式、方法名称和参数(在WSDL里的称呼有所不同)等等。WSDL也是以XML为基础的。

服务发布和发现层协议是供注册中心这个角色使用的,UDDI是目前使用最广泛的注册中心,图中其他几种方式也有应用。

服务组装层用于组装Web服务成为新的服务,这些被组装起来的服务一般体现了一定的业务流程。其好处是各服务间耦合很小,改变起来十分容易。在这一层里,目前有不少协议正在竞争,BPEL4WS可以说具有一定的优势吧。

待开发的协议与我们比较小,暂时不说了。图中右边三个纵向协议贯穿整个Web服务生命周期,它们是服务管理、服务质量和服务安全。因为将来很多的Web服务是要收费才可以使用的,和钱挂钩的东西就必须能够管理、保证质量和安全才行。一直以来,它们都是Web服务研究的难点(因为涉及到太多方面的利益),目前在功能方面Web服务已经做好了准备,如果能够攻破这些非功能性的难题,我想Web服务距离大规模应用就不远了。

图1是比较常见的一种协议栈图,实际上由于Web服务的使用方式多种多样,协议栈图也未必相同。例如w3.org上的是这样,它把XML也技术表现在图上,体现了其在Web服务中的基础地位。

总结一下Web服务的关键技术:XML、SOAP、WSDL、UDDI和BPEL4WS。

如果觉得这一篇有点抽象,那很正常,因为出现了新的概念。另外,我自己对Web服务的理解也是来源与书本,项目经验不足,缺少对这个行业的宏观认识,所以在写出来的时候都要斟酌一二,拿不准的尽量不写。没关系,下面几篇讲的是具体技术,可以醒醒了:)

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/08/05/30402.html

Web服务系列(一) 简介

file

Web服务系列计划由大约十篇帖子组成,目的是介绍各种概念,以及开发Web服务的工具。在这第一篇里我会简单介绍一下Web服务的概念,并演示一个Web服务的具体应用场景。文章的篇幅不会太长,因为那样会看得很累,反正我是个“没有耐心”的人,呵呵。

Web服务(Web Services)在很多人眼里还是个十分神秘的概念,究其根源,我想主要是由于Web服务被宣传得很多,但实际应用却鲜见,给人一种很复杂和难以理解的感觉。另外,Web服务是基于XML的,不少人对XML本身也缺乏理解,虽然他们可能每天都在写XML格式的配置文件。

提到Web服务的起源就一定要先说一说SOA(面向服务的体系结构),和很多具有划时代意义的软件技术一样,SOA的出现根本上也是为了解决软件危机问题。做过项目的人都有过这种感受,随着项目推进,模块之间关系越来越紧密,任何一个小的修改都可能引起整个系统的不稳定,而客户需求偏偏总是在改变,结果是项目以差不多失败的结果告终。

从(分布式)软件发展的趋势来看,C/S->B/S->SOA,模块之间的耦合度是由紧密到松散的,松散的耦合有利于修改。我们常说的各种设计模式,其中大部分不也是为了降低类之间的耦合度吗。

这里我引用一下IBM网站上对SOA的定义:

面向服务的体系结构(service-oriented architecture)是一个组件模型,它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种这样的系统中的服务可以以一种统一和通用的方式进行交互。(全文

说得通俗一点就是,系统中分为三种角色:服务提供者服务使用者注册中心,提供者发布服务到注册中心,使用者通过注册中心发现所需服务,然后与该服务的提供者绑定,并调用服务。

那么Web服务和SOA是什么关系呢,可以这样说,Web服务是SOA的一种实现,有点像Tomcat和JSP/Servlet规范的关系。SOA是一个比较虚的概念,例如它只提出定义一些接口和协议,那么这些东西具体应该怎样定义呢,Web服务就将它们具体化了:Web服务使用的协议都是基于XML的;SOA只说应该有三种角色,而Web服务里这三种角色都有具体的实现方式。看到这里你应该会问,那么SOA还有哪些实现呢?CORBA、DCOM和J2EE都可以算是,但我认为它们不能算很纯粹,至少它们并不都具有中立的协议。

现在用一个具体的例子来说明一下Web服务。假设我们的系统中需要一项功能是查询当地的天气情况(世界时间、货币汇率等等,都一样),显然我们不会自己做一个从气象部门数据库中查找数据的程序,这需要很多手续也没有必要,更要命的是,这样做会增加我们与气象部门的耦合度。试想某一天气象部门的数据库结构改变了,我们将不得不修改自己的代码,如果他们忘记通知我们这一改变,想象一下客户会看到什么?

为了利用Web服务,我们从某一注册中心查找和天气有关的服务,在结果中也许我们会选择收费较低,或者收费稍高但更稳定和准确的服务。从注册中心我们能够得到所选服务的完整描述,其中包含了各种数据类型和调用方式,利用这些信息,可以使用工具生成这些必要的类,以及客户端Stub,利用这个Stub就可以调用远程的Web服务了。在我们的例子中,调用后服务提供者会返回一个含有结果的消息,在我们的系统中可以从这个消息里得到所要的结果,并显示给客户。这样就形成一个完整的Web服务调用。这种调用方式被称为静态调用,因为在Stub里服务提供者的地址(被称为调用端点endpoint)是写定的,还有另外一种方式被称为动态调用,以后会讲到。

那么Web服务和以前的RPC(远程过程调用)有什么分别呢?RPC通常要求调用者和被调用者是同构的,即使用同样的语言编写,而Web服务没有这个要求(诀窍在于使用了XML封装消息),这就大大增加了灵活程度;另外,Web服务的调用除这种类似RPC的方式外,还可以是基于消息的方式,服务使用者可以只接收消息,或是只发送消息,在一些应用中这种方式十分有用。

好了,把这次讲的内容总结一下就是:Web服务是SOA的实现,Web服务不是RPC

下次将稍微详细点说说Web服务的结构和协议栈,第三篇文章开始会陆续讲些与Web服务关系非常紧密的几个概念。本人水平有限,如果存在错误欢迎指出,转载请注明出处。最后推荐一个学习Web服务的好地方,IBM开发者SOA与Web服务专区,适合各种水平的读者阅读,同时有很多最新应用,绝对值得一去。在http://www.xmethods.net/有很多Web服务的演示,有兴趣也可以看看。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/08/04/30012.html

Apache Struts常见异常信息和解决方法

file

以下所说的struts-config.xml和ApplicationResources.properties等文件名是缺省时使用的,如果你使用了多模块,或指定了不同的资源文件名称,这些名字要做相应的修改。

1、“No bean found under attribute key XXX”
在struts-config.xml里定义了一个ActionForm,但type属性指定的类不存在,type属性的值应该是Form类的全名。或者是,在Action的定义中,name或attribute属性指定的ActionForm不存在。

2、“Cannot find bean XXX in any scope”
在Action里一般会request.setAttribute()一些对象,然后在转向的jsp文件里(用tag或request.getAttribute()方法)得到这些对象并显示出来。这个异常是说jsp要得到一个对象,但前面的Action里并没有将对象设置到request(也可以是session、servletContext)里。
可能是名字错了,请检查jsp里的tag的一般是name属性,或getAttribute()方法的参数值;或者是Action逻辑有问题没有执行setAttribute()方法就先转向了。
还有另外一个可能,纯粹是jsp文件的问题,例如<logic:iterate>会指定一个id值,然后在循环里<bean:write>使用这个值作为name的值,如果这两个值不同,也会出现此异常。(都是一个道理,request里没有对应的对象。)

3、“Missing message for key "XXX"”
缺少所需的资源,检查ApplicationResources.properties文件里是否有jsp文件里需要的资源,例如:

<bean:message key="msg.name.prompt"/>

这行代码会找msg.name.prompt资源,如果AppliationResources.properties里没有这个资源就会出现本异常。在使用多模块时,要注意在模块的struts-config-xxx.xml里指定要使用的资源文件名称,否则当然什么资源也找不到,这也是一个很容易犯的错误。

4、“No getter method for property XXX of bean teacher”
这条异常信息说得很明白,jsp里要取一个bean的属性出来,但这个bean并没有这个属性。你应该检查jsp中某个标签的property属性的值。例如下面代码中的cade应该改为code才对:

<bean:write name="teacher" property="cade" filter="true"/>

5、“Cannot find ActionMappings or ActionFormBeans collection”
待解决。

6、“Cannot retrieve mapping for action XXX”
在.jsp的<form>标签里指定action='/XXX',但这个Action并未在struts-config.xml里设置过。

7、HTTP Status 404 - /xxx/xxx.jsp
Forward的path属性指向的jsp页面不存在,请检查路径和模块,对于同一模块中的Action转向,path中不应包含模块名;模块间转向,记住使用contextRelative="true"

8、没有任何异常信息,显示空白页面
可能是Action里使用的forward与struts-config.xml里定义的forward名称不匹配。

9、“The element type "XXX" must be terminated by the matching end-tag "XXX".”
这个是struts-config.xml文件的格式错误,仔细检查它是否是良构的xml文件,关于xml文件的格式这里就不赘述了。

10、“Servlet.init() for servlet action threw exception”
一般出现这种异常在后面会显示一个关于ActionServlet的异常堆栈信息,其中指出了异常具体出现在代码的哪一行。我曾经遇到的一次提示如下:

java.lang.NullPointerException
    at org.apache.struts.action.ActionServlet.parseModuleConfigFile(ActionServlet.java:1003)
    at org.apache.struts.action.ActionServlet.initModuleConfig(ActionServlet.java:955)

为解决问题,先下载struts的源码包,然后在ActionServlet.java的第1003行插入断点,并对各变量进行监视。很丢人,我竟然把struts-config.xml文件弄丢了,因此出现了上面的异常,应该是和CVS同步时不小心删除的。

11、“Resources not defined for Validator”
这个是利用Validator插件做验证时可能出现的异常,这时你要检查validation.xml文件,看里面使用的资源是否确实有定义,form的名称是否正确,等等。

上面这些是我在用Struts做项目时遇到过的问题,其中一些曾困绕我不少时间,其实大部分都是自己不细心造成的。希望这篇文章能对你的开发有所帮助,并欢迎继续补充。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/08/02/29566.html