当柳上原的风吹向天际的时候...

真正的快乐来源于创造

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  368 Posts :: 1 Stories :: 201 Comments :: 0 Trackbacks

#

大家自己试一试吧:

ASCII Escaped UNICODE 的转换,不用native2ascii.exe
posted @ 2009-10-03 08:34 何杨| 编辑 收藏

Web Service定义了一套标准的调用过程:
  • 服务器端首先用一套标准的方法向外界描述它所提供的服务的内容,这属于WSDL的范畴。
  • 客户端需要以一种标准的协议来调用此服务,这属于SOAP的范畴。
  • 服务提供者将服务内容放在一个公共的网址上让人查询,这属于UDDI的范畴。
WSDL:服务内容的标准化描述
WSDL的目的是告诉外界自己能提供什么样的服务,有点类似于java的接口。
WSDLd的全称是Web Service Description Language,是一种基于XML的关于Web服务的描述,主要目的在于将自己的Web服务的所有相关内容如提供服务的传输方式,服务方法接口,接口参数,服务路径等,生成相应的完全的文档,发布给使用者。使用者可以通过这个WSDL文档,创建相应的SOAP请求消息,通过HTTP传递给Web服务提供者;Web服务提供者在完成请求服务后,将SOAP返回消息传回给请求者,服务请求者再根据WSDL文档将SOAP返回消息解析成程序能够理解的内容。

SOAP:标准的传输协议
SOAP是标准化的消息协议,是客户端送给服务器希望调用的类和方法的一种消息格式,也包括服务返回的消息格式。之所以有SOAP是因为只有大家都遵守一套消息格式的标准,相互之间才能明白对方想要干什么。
SOAP是Web Service的标准传输协议,是Simple Object Application Propotol的简写,是一种标准化的传输消息的XML格式。
SOAP请求消息将客户端的服务请求消息发给服务器,比如想调用什么接口,以及接口的参数值等。SOAP答复消息是从服务器返回给客户端的消息,如接口实现类调用后的返回值或是调用服务时的错误信息等。定义WSDL是很重要的,一旦WSDL定义好了,再根据WSDL的输入变量和输出变量的结构就可以知道SOAP的请求消息和响应消息的格式了。

UDDI:服务的公共网址
UDDI是Universal Description Discovery and Intergretion的缩写,是一种创建注册服务的规范,以便大家将自己的Web Service进行注册发布供使用者查找。
当服务提供者想将自己的Web Service发布,以便外部能找到其服务时,那么服务提供这可以将自己的Web Service注册到相应的UDDI商用注册网站。
UDDI并非一个必须的Web Service组件,服务方完全可以不进行UDDI的注册。因为WSDL文件中已经给出了Web Service的地址URI,外部可以通过它进行相应的Web Service调用。

以下是一个Web Service示例程序,主要参考了梁爱虎的《SOA 思想,技术与系统集成应用详解》中的例子:

发布Web服务的类接口:
package com.heyang;

/**
 * 生成序列号的接口
 * 
@author: 何杨(heyang78@gmail.com)
 * @date: 2009-9-29-下午12:37:55
 
*/
public interface ISerial{
    
/**
     * 传入类型,返回序列号
     * 
@param type
     * 
@return
     
*/
    
public String getSN(String type);
}

发布web服务的类:
package com.heyang;

import java.text.MessageFormat;

/**
 * ISerial的实现类
 * 
@author: 何杨(heyang78@gmail.com)
 * @date: 2009-9-29-下午12:40:05
 
*/
public class SerialService implements ISerial{
    
private static int number;
    
    
/**
     * 产生SN:CD-000001的序列号
     * MessageFormat的用法可参考http://hi.baidu.com/gacmotor/blog/item/372b4a3a0b010de314cecb0b.html
     
*/
    
public String getSN(String type) {
        number
++;
        Object[] arr
=new Object[]{type,number};
        String result
=MessageFormat.format("SN:{0}-{1,number,000000}",arr);
        
return result;
    }
    
    
public static void main(String[] args){
        SerialService s
=new SerialService();        
        System.out.println(s.getSN(
"CD"));
    }
}

Web.xml:
<?xml version="1.0" encoding="UTF-8"?>

<web-app > 
    
<servlet>
        
<servlet-name>AxisServlet</servlet-name>
        
<servlet-class>
            org.apache.axis.transport.http.AxisServlet
        
</servlet-class>
    
</servlet>

    
<servlet-mapping>
        
<servlet-name>AxisServlet</servlet-name>
        
<url-pattern>/servlet/AxisServlet</url-pattern>
    
</servlet-mapping>

    
<servlet-mapping>
        
<servlet-name>AxisServlet</servlet-name>
        
<url-pattern>*.jws</url-pattern>
    
</servlet-mapping>
    
    
<servlet-mapping>
        
<servlet-name>AxisServlet</servlet-name>
        
<url-pattern>/services/*</url-pattern>
    
</servlet-mapping>
</web-app>

wsdd文件server-config.wsdd
<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
    xmlns:java
="http://xml.apache.org/axis/wsdd/providers/java">
    
<handler type="java:org.apache.axis.handlers.http.URLMapper"
        name
="URLMapper" />
    
<service name="fetchSerialNumber" provider="java:RPC">
        
<parameter name="className" value="com.heyang.SerialService" />
        
<parameter name="allowedMethods" value="getSN" />
    
</service>
    
<transport name="http">
        
<requestFlow>
            
<handler type="URLMapper" />
        
</requestFlow>
    
</transport>
</deployment>

测试类:
package com.heyang.client;

import java.net.MalformedURLException;
import java.rmi.RemoteException;

import javax.xml.rpc.ServiceException;

import org.apache.axis.client.Call;
import org.apache.axis.client.Service;

/**
 * WebServiceClientTest
 * 
@author: 何杨(heyang78@gmail.com)
 * @date: 2009-9-29-下午01:03:05
 
*/
public class WebServiceClientTest {
    
public static void main(String[] args) throws ServiceException,
            MalformedURLException, RemoteException {
        
// 标识Web Service的具体路径
        /**
         * SerialNumber:发布到Tomcat上的war的名称,采用工程名
         * services:固定写法,与Web.xml中设定对应
         * fetchSerialNumber:server-config.wsdd中设定的service名
         
*/
        String endpoint 
= "http://localhost:8080/SerialNumber/services/fetchSerialNumber";

        
// 创建 Service实例
        Service service = new Service();
        
        
// 通过Service实例创建Call的实例
        Call call = (Call) service.createCall();
        
        
// 将Web Service的服务路径加入到call实例之中.
        call.setTargetEndpointAddress(new java.net.URL(endpoint));// 为Call设置服务的位置
        
        
// 调用Web Service的方法
        call.setOperationName("getSN");
        
        
// 调用Web Service,传入参数
        String retval = (String) call.invoke(new Object[] { "CD" });
        
        System.out.println(retval);
    }
}

输出示例:
SN:CD-000004


例程下载(使用Axis,注意Tomcat的lib目录中要包括mail.jar和activation.jar):
http://www.blogjava.net/Files/heyang/SerialNumber20090929130453.rar

使用说明:
使用Ant脚本将jar包打好,再部署到Tomcat中,可以用http://localhost:8080/SerialNumber/services来测试一下是否有输出,有则表示部署成功,之后执行WebServiceClientTest。

主要参考资料:
梁爱虎著《SOA 思想,技术与系统集成应用详解》
posted @ 2009-09-29 14:09 何杨| 编辑 收藏

XPath的概念
XPath是一种定位XML文档的各个部分的语言,它还提供处理字符串,数字和布尔值的常用函数。XPath可以定位XML文档的元素和属性等部件。XPath提供的一星儿函数能够定位返回字符串或检测出字符串的长度,XPath还包括字符数据转换成数字类型和布尔类型的函数。
XPath从它的路径符号给出名称,它使用的语法铜URL类似,通常称XPath语句为XPath表达式。
XPath不是一种独立技术,它用于其它的技术如XSLT中,用XPath可以检索XML的元素和属性,可以XPath看做是XML的SQL。

XPath语法
1.定位路径:他是一种选择节点集合的XPath表达式类型,通常使用“/”隔开的定位阶来构造定位路径。如/games/game,上述定位路径有两个定位阶。定位路径有两种类型:相对的和绝对的。相对的定位路径是由“/”分开的一个或多个定位阶的序列。绝对定位路径由“/”和后面的相对定位路径组成。

2.定位阶:他是定位路径的组成部分,由三部分组成:
  1)一个轴:它规定了上下文节点和定位阶所选节点之间的关系。
  2)一个节点测试:它规定了定位阶选择的阶段类型。
  3)零或多个谓词,它们使用了表达式进一步改进节点的选择。
  如XPath表达式"/games/game[@genre='rpg']",该表达式选择了所有genre属性值为rpg的位于games元素下的game元素。

3.节点测试:它通知XPath表达式运算的节点类型,节点测试通常都是源树种元素名称,如/games/game,该表达式有两个节点测试:games和game。

4.轴说明符:它用于明确指出要处理的节点测试的关系,如果要选games元素的所有子元素,则代码如下:child::games.
  下面归纳了部分轴说明符
  轴说明符           说明
  child              节点的所有子节点
  descendant         节点的所有后代节点
  parent             节点的父节点
  following-sibling  节点后面的兄弟节点
  preceding-sibling  节点前面的兄弟节点

5.谓词:它是另一种进一步缩小节点选择范围的方法。XPath使用方括号[]来指定谓词,谓词使用XPath提供的函数提供测试,它可以访问属性(使用@符号)。
如前面出现过的XPath表达式"/games/game[@genre='rpg']",方括号[]中的“@genre='rpg'”就是谓词,该表达式用于测试位于games元素下的game元素的genre属性值是否为rpg。

XPath函数
XPath函数使得用户能够处理字符串,数字和节点集合,下面列出了三个节点集合函数:
number last()           返回最后位置 如count(games/game)返回games元素下的game元素数目
number position()       返回当前位置 如games/game[last()]返回games元素下的最后一个game元素
number count(node-set)  返回结合参数中节点数目 如/games/game[position()=1]将返回games元素下的首个game元素。

下面使用开源包dom4j对XML文档进行XPath操作的例子

将要处理的XML文档如下:
<?xml version="1.0" encoding="GBK"?>

<employees> 
  
<employee lable="tech">
    
<id>0001</id>
    
<name>李白</name>
    
<title>程序员</title>
    
<skill>Java</skill>
    
<age>18</age>
  
</employee>
  
<employee lable="tech">
    
<id>0002</id>
    
<name>杜甫</name>
    
<title>高级程序员</title>
    
<skill>C#</skill>
    
<age>28</age>
  
</employee>
  
<employee lable="tech">
    
<id>0003</id>
    
<name>Andy</name>
    
<title>项目经理</title>
    
<skill>PPT</skill>
    
<age>38</age>
  
</employee>
  
<employee lable="admin">
    
<id>0004</id>
    
<name>Bill</name>
    
<title>部门经理</title>
    
<skill>Java</skill>
    
<age>48</age>
  
</employee>
  
<employee lable="admin">
    
<id>0005</id>
    
<name>Cindy</name>
    
<title>总经理</title>
    
<skill>C#</skill>
    
<age>58</age>
  
</employee>
</employees>

XPath示例程序:
package com.heyang;

import java.util.Iterator;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;

/**
 * dom4j的XPath解析示例
 * 
@author: 何杨(heyang78@gmail.com)
 * @date: 2009-9-27-上午09:59:52
 
*/
public class XPathSample{
    
public static void main(String[] args) throws Exception{
        SAXReader reader 
= new SAXReader();
        Document  doc 
= reader.read(XPathSample.class.getResource("/Employee.xml").getPath());
        
        
// 1.XPath选择器,选择根节点employees下所有属性lable为tech的employee子节点
        System.out.println("1.选择根节点employees下所有属性lable为tech的employee子节点");
        XPath xpathSelector 
= DocumentHelper.createXPath("/employees/employee[@lable='tech']");
        List results 
= xpathSelector.selectNodes(doc);
        
for ( Iterator iter = results.iterator(); iter.hasNext(); ) {
          Element element 
= (Element) iter.next();
          
// 找到employee子节点的name子节点的文字打印
          System.out.println(element.selectSingleNode("./name").getText());
        }
        
        
// 2.打印名称为李白的employee节点的title
        System.out.println("2.打印名称为李白的employee节点的title");
        String title 
= doc.valueOf( "//employee[name='李白']/title" );
        System.out.println(title);

        
// 3.XPath选择器,选择所有属性lable为admin的employee子节点的name子节点
        System.out.println("3.选择所有属性lable为admin的employee子节点的name子节点");
        XPath xpathSelector2 
= DocumentHelper.createXPath("//employee[@lable='admin']/name");
        List results2 
= xpathSelector2.selectNodes(doc);
        
for ( Iterator iter = results2.iterator(); iter.hasNext(); ) {
            Element element 
= (Element) iter.next();
            System.out.println(element.getText());
        }
        
        
// 4.XPath选择器,选择所有年龄节点大于20的employee子节点的name子节点
        System.out.println("4.选择所有年龄节点大于20的employee子节点的name子节点");
        XPath xpathSelector3 
= DocumentHelper.createXPath("//employee[age>20]/name");
        List results3 
= xpathSelector3.selectNodes(doc);
        
for ( Iterator iter = results3.iterator(); iter.hasNext(); ) {
            Element element 
= (Element) iter.next();
            System.out.println(element.getText());
        }
    }
}

控制台输出:
1.选择根节点employees下所有属性lable为tech的employee子节点
李白
杜甫
Andy
2.打印名称为李白的employee节点的title
程序员
3.选择所有属性lable为admin的employee子节点的name子节点
Bill
Cindy
4.选择所有年龄节点大于20的employee子节点的name子节点
杜甫
Andy
Bill
Cindy

XPath相关参照文档(网络上搜索得到,不保证内容的完全正确性):
表达式            描述
节点名            选择所有该名称的节点集
/                 选择根节点
//                选择当前节点下的所有节点
.                 选择当前节点
..                选择父节点
@                 选择属性
示例
表达式            描述
bookstore         选择所有bookstore子节点
/bookstore        选择根节点bookstore
bookstore/book    在bookstore的子节点中选择所有名为book的节点
//book            选择xml文档中所有名为book的节点
bookstore//book   选择节点bookstore下的所有名为book为节点
//@lang           选择所有名为lang的属性

方括号[],用来更进一步定位选择的元素
表达式                            描述
/bookstore/book[1]                 选择根元素bookstore的book子元素中的第一个(注意: IE5以上浏览器中第一个元素是0)
/bookstore/book[last()]            选择根元素bookstore的book子元素中的最后一个
/bookstore/book[last()-1]          选择根元素bookstore的book子元素中的最后第二个
/bookstore/book[position()<3]      选择根元素bookstore的book子元素中的前两个
//title[@lang]                     选择所有拥有属性lang的titile元素
//title[@lang='eng']               选择所有属性值lang为eng的title元素
/bookstore/book[price>35.00]       选择根元素bookstore的book子元素中那些拥有price子元素且值大于35的
/bookstore/book[price>35.00]/title 选择根元素bookstore的book子元素中那些拥有price子元素且值大于35的title子元素

选择位置的节点
通配符               描述
*                    匹配所有元素
@*                   匹配所有属性节点
node()               匹配任何类型的节点
示例
表达式               描述
/bookstore/*         选择根元素bookstore的下的所有子元素
//*                  选择文档中所有元素
//title[@*]          选择所有拥有属性的title元素
使用操作符“|”组合选择符合多个path的表达式

posted @ 2009-09-27 10:56 何杨| 编辑 收藏

在Spring的Bean装配文件中配置组件是合适的,但类似于数据库细节,队列主题细节等应该分离出来,这时我们可以使用Spring的PropertyPlaceholderConfigurer从外部属性文件中装载一些配置信息,使用起来很简单,请参考下例:
http://www.blogjava.net/Files/heyang/SpringProperties20090926133054.rar

注意加入必要的包:commons-logging-1.0.4.jar,log4j-1.2.14.jar,spring.jar

两个配置的内容:
person1.properties
person1.id=001
person1.name
=Andy
person1.password
=123456

person2.properties
person2.id=002
person2.name
=Bill
person2.password
=111111

Bean装配文件的内容:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        
<property name="locations">
            
<list>
                
<value>person1.properties</value>
                
<value>person2.properties</value>                
            
</list>
        
</property>
    
</bean>
    
<bean id="person1" class="com.heyang.Person" >    
        
<property name="id">
            
<value>${person1.id}</value>
        
</property>
        
<property name="name">
            
<value>${person1.name}</value>
        
</property>
        
<property name="password">
            
<value>${person1.password}</value>
        
</property>
    
</bean>
    
    
<bean id="person2" class="com.heyang.Person" >    
        
<property name="id">
            
<value>${person2.id}</value>
        
</property>
        
<property name="name">
            
<value>${person2.name}</value>
        
</property>
        
<property name="password">
            
<value>${person2.password}</value>
        
</property>
    
</bean>
 
</beans>

Person类:
public class Person{
    
private String name;
    
private String id;
    
private String password;
    
    
public String toString(){
        
return "Person id="+id+" name="+name+" password="+password;
    }
    
    
public String getId() {
        
return id;
    }
    
public void setId(String id) {
        
this.id = id;
    }
    
public String getName() {
        
return name;
    }
    
public void setName(String name) {
        
this.name = name;
    }
    
public String getPassword() {
        
return password;
    }
    
public void setPassword(String password) {
        
this.password = password;
    }
    
    
public static void main(String[] args){
        ApplicationContext appContext 
= new ClassPathXmlApplicationContext("bean.xml");
        
        Person person1
=(Person)appContext.getBean("person1");
        System.out.println(person1);
        
        Person person2
=(Person)appContext.getBean("person2");
        System.out.println(person2);
    }
}

控制台输出:
Person id=001 name=Andy password=123456
Person id
=002 name=Bill password=111111

posted @ 2009-09-26 13:34 何杨| 编辑 收藏

命名服务概述
命名服务(Naming Service)是可以将复杂数据对象或其引用关联到已知名称的机制.然后可以发布这些名称,客户可以使用这些名称查询与它们相关联的数据对象.名称与对象之间的关联称为绑定.命名服务通常与其它服务(如文件系统,目录和数据库等)集成以提供这种绑定.大家可以从现代图书馆的卡片目录系统来理解命名服务.

JNDI介绍(Java Naming and Directory Inteface,Java命名与目录接口)
JNDI是Java命名与目录接口(Java Naming and Directory Inteface)的缩写,有时也简称Java名录服务,J2EE组件通过调用JNDI提供的查找(lookup)方法以定位对象。JNDI是专门为Java设计的,一个Java应用程序可以用JNDI检索Java对象.JNDI还可以执行标准目录操作,如关联属性和对象,并用对象的属性搜索它们.
JNDI名字是对象的友好名字,这些名字通过J2EE服务器提供的命名目录服务绑定到各自的对象上。由于J2EE组件是通过JNDI编程接口访问服务的,所以通常情况下把对象的友好名字称之为JNDI名字。比如,mydatabase数据库的JNDI名字为jdbc/mydatabase,一旦J2EE服务器启动,系统自动从配置文件读取相关信息,并将jdbc/mydatabase的JNDI数据库名字添加到名字空间。
Sun公司对JNDI的定义为”一种对Java平台的标准扩展,它为Java技术编写的应用程序提供了对企业中多种命名和目录服务的统一接口.作为Java Enterprise API集的一部分,JNDI使与异构企业命名和目录服务的无缝连接提供了可能”.

连接工厂(Connection Factory)
连接工厂(Connection Factory)是用于产生链接对象,使得J2EE组件可以访问资源的一种对象。比如,用于数据库的连接工厂是javax.sql.Database对象,它产生java.sql.Connection对象。

JNDI和Weblogic Server
Weblogic提供了在JNDI规范中规定的实现.这使Java客户可以用标准JNDI调用连接到Weblogic Server.客户可以在Weblogic命名空间中访问Weblogic命名服务并使对象可用,还可以检索它们.
如果希望访问已经加载到Weblogic Server的JNDI树中的对象的Java客户一般要执行以下任务:
1.与服务器建立一个上下文
2.对JNDI树进行查询或者更新

取得上下文例程
这是在命名空间中访问绑定对象的第一步.应用程序将获得引导上下文称为InitialContext.它是从InitialContext工厂获得的.这个工厂使用几个属性标识上下文需要指向的Weblogic Server.
Hashtable ht=new Hashtable();
ht.put(Context.INITIAL_CONTEXT_FACTORY, 
"weblogic.jndi.WLInitialContextFactory");
ht.put(Context.PROVIDER_URL, 
"t3://127.0.0.1:7001");

Context ctx
=null;   

try {
    ctx
=new InitialContext(ht);
catch (NamingException e) {
    e.printStackTrace();
    System.out.println(
"不能得到上下文");
}

创建一个绑定例程
要在WeblogicJNDI树中创建一个新的绑定,可使用Context.bind方法.这个方法以新绑定的名称以及绑定到这个名称号的对象为参数.注意这个对象必须是可序列化的,也就是说它必须实现java.io.Serializablejie接口(实现这个接口无需实现任何方法,它只是告诉JVM这个对象可以序列化).
Hashtable ht=new Hashtable();
ht.put(Context.INITIAL_CONTEXT_FACTORY, 
"weblogic.jndi.WLInitialContextFactory");
ht.put(Context.PROVIDER_URL, 
"t3://127.0.0.1:7001");
Context ctx
=null;   
try {
  ctx
=new InitialContext(ht);  
  String text
="菩提本非树,明镜亦非台,本来无一物,何处染尘埃.";   
  ctx.bind(
"TEST", text);
catch (NamingException e) {
  e.printStackTrace();
  System.out.println(
"不能得到上下文");
}

下面是在Weblogic中查看刚才创建的绑定内容图示
一.点击环境,服务器,点击“查看JNDI树链接”


二.可以看到已经绑定到JNDI树中的对象

删除现有的绑定例程
使用Context.unbind方法可以从JNDI树中删除绑定,不能再从树中访问这些对象了.
Hashtable ht=new Hashtable();
ht.put(Context.INITIAL_CONTEXT_FACTORY, 
"weblogic.jndi.WLInitialContextFactory");
ht.put(Context.PROVIDER_URL, 
"t3://127.0.0.1:7001");

Context ctx
=null;   
try {
  ctx
=new InitialContext(ht);  
  ctx.unbind(
"TEST");
catch (NamingException e) {
  e.printStackTrace();
  System.out.println(
"不能得到上下文");
}

总结
JNDI为应用程序提供了标准统一的方式,连接和使用企业中存在的多个对象目录的能力.WeblogiocServer提供了JNDI的实现,客户机可以和它无缝连接.同一客户机还可以用JNDI API连接到另一命名服务上.Weblogic Server大量利用JNDI树完成其常规功能.如果应用程序使用EJB,那么这个EJB就发布在JNDI树中,类似的其它对象如DataSource对象,事务对象都发布在JNDI树上.

参考例程
WeblogicJNDI(注意:weblogic.jar请自行加入lib目录)

posted @ 2009-09-25 09:16 何杨| 编辑 收藏



消息服务

消息服务是以可靠的,异步的,松耦合的,语言无关的,平台无关的以及通常是可配置的方式在分布式应用程序之间传递消息的软件。消息服务通过封装在发送者和接受者之间传递的消息,并提供位于分布式消息客户之间的软件层完成这一任务。消息服务还提供了让消息客户使用的接口,它隔离了底层消息服务实现。这样的基础结构还可以看成是事件通知类型的服务,其中消息是事件,在消息客户间发送这些消息是一种事件通知机制。

理解消息传递

消息传递是一种在软件组件或者应用之间进行通信的方法。一个消息传递系统是一个点对点的设施:一个消息传递客户端可以发送消息到任何其它客户端,或者接收来自它们的消息。每个客户端连接到一个消息传递代理,消息传递代理提供了创建,发送,接受和阅读消息的工具。
消息传递支持松耦合的分布式通信。组件发送消息到某个目的地,接收方可以从目的地获取消息。但是,发送方和接收方在通信时并不需要同时可用。实际上,发送 方不需要了解任何有关接收方的内容,而接收方也不需要了解任何有关发送方的内容。发送方和接收方只需知道要使用什么样的消息格式以及什么目的地。
消息传递和电子邮件不同,它是一种在人们之间或软件和人们之间建立通信的方法,而消息传递则用于软件应用或软件组件之间的通信。

使用消息服务中间件的消息服务实现
一些中间件软件(Weblogic,Sonic MQ等)实现了异步接收由消息生产者生成的消息,并将它们路由给消息消费者的消息服务功能。消息客户通过消息客户接口,以透明的方式利用中央消息服务的服务。
通过致力于使用中间件中的中央管理的持久和冗余的机制,消息中间件提高了消息服务的可靠性和可用性。消息中间件允许消息客户与消息服务中间件服务器之间的连接,还减轻了客户管理到多个消息服务端点位置的连接的需要。
(补图)

Message-Oriented Middleware,MOM
面向消息的中间件和消息服务几乎是同义词,MOM就是消息服务的一种实现,尽管采取的是特定类型的MOM系统的标准方式。MOM API定义了分布式应用程如何使用底层MOM消息信道或者队列彼此通信。
消息在应用程序之间通过MOM传递,不会使消息的发送者阻塞,也就是说,发送者可以发送一个消息并让MOM保证它到达预定的接受者,而不用等待接受者的响应。

Java Message Service(JMS)
从J2EE1.3版本开始,JMS API开始成为该平台的一部分,应用开发者可以通过JavaEE组件来使用消息传递。
当JMS API在1998年引入时,它最重要的用途是允许Java应用访问现有的面向消息中间件系统,如来自IBM的MQSeries,后来JMS产品可以为企业应用提供一个完整的消息传递功能。
Java Message Service(JMS)是一个定义了消息客户如何用标准方式与底层消息服务提供者通信的Java API。
JMS还提供了一个接口,底层消息服务提供者实现了这个接口,向客户提供JMS服务。
JMS提供了点到点和发布-订阅模型。在JMS规范中,这种消息模型也称为消息域。点到点消息是通过实现消息队列完成的,生产者在队列中写入消费者接收的消息;发布-订阅模型是通过实现主题节点的层次结构完成的,其中生产者发布消息,消费者可以订阅这些消息。
JMS提供了核心抽象消息API,点到点消息队列模型API和发布-订阅模型API都扩展这个API。

什么是JMS API
Java消息服务API允许应用创建发送,接收和阅读消息。它的特点有:
1.异步:JMS供应者在接受到消息时可以将它们传递到某个客户端,客户端不必为了接收这些消息而主动发出请求。
2.可靠:JMS API可以确保某个消息传递而且只传递一次。对于那些能够忍受消息丢失或接受到重复消息的应用,也可以使用较低级别的可靠性。

何时使用JMS API
在以下情况下,企业应用供应者更可能选择消息传递API而不是某种紧耦合API:
1.供应者希望组件不依赖有关其他组件接口的信息,这样可以很容易替换组件。
2.供应者希望应用在不管所有组件是否已经就绪并且同时运行的情况下都能够运行。
3.应用业务模型允许组件发送信息到其它组件并且在不必接收到立即响应的情况下继续工作。

基本的JMS API概念
编写基本的JMS客户端应用必须了解以下概念:
JMS API 体系结构
消息传递域
消息使用

JMS API体系结构
JMS应用由以下部分所组成:
1.JMS供应者(JMS Provider):它是一个实现了JMS接口并提供管理和控制功能的消息传递系统。
2.JMS客户端(JMS Client):是以Java编程语言编写,负责生成和是哟个消息的程序或组件。任何JavaEE应用组件都可以作为JMS客户端。
3.消息(Message):是在JMS客户端之间进行信息沟通的对象。
4.管理对象(Administrered Object)是预先配置好的JMS对象,它是由管理员针对客户端的使用而创建的。JMS管理对象的两种类型有目的地(Destination)和连接工厂(ConnectionFactory)。


两种消息传递域(模式)

点对点消息传递域:这种消息传递是建立在消息队列,发送方和接收方的概念上的。每个消息都被寻址到某个特定的队列,并且负责接收的客户端从保存消息的队列中提取这些消息。在消息被使用或过期之前,队列会移植保存所有发送给它们的消息。它的特点有:
1.每个消息只有一个使用者(消费者),即一条消息只会被一个使用者(消费者)使用。
2.消息的发送方和接收方并没有时间同步的依赖性。当发送方发送消息时,不管接受方是否在运行,后者都可以接收到所发送的消息。
3.接收方在成功处理完消息后会发出确认。
4.这种模式适合多个发送者对一个消费者的情况。

发布/订阅消息传递域:这种消息传递方式中客户端按照某个主题来处理消息,发送者和订阅者通常是匿名的,而且可以动态的发布或订阅内容层次。系统负责将来自某个主题的多个发布者的消息分发到该主题的多个订阅者。主题只要负责将消息分发到当前订阅者,便同时负责保存这些消息。它的特点有:
1.每个消息可以有多个使用者。
2.发布者和订阅者具有时间同步依赖性。订阅了某个主题的客户端只有在客户端创建了订阅之后才能使用所发布的消息,而且订阅者必须继续保持活动状态才能使用消息。
3.这种模式适合一个发送者对多个消费者的情况。

图:两种JMS类层次结构



消息使用

消息传递产品本质上是异步的:在消息的生成和使用之间并没有基本的时间同步依赖性,JMS规定消息可以按照以下方式之一来使用:
同步:订阅者或接收方通过调用receive方法明确获取来自目的地的消息。Receive方法在消息到来之前可以进入阻塞状态,或者在消息未在指定时间范围内到达的情况下进入超时状态。
异步:客户端可以向使用者注册一个消息监听器(message listener)。消息监听器类似于事件监听器,当消息到达目的地时,JMS供应者通过调用监听器的onMessage方法来传递消息。

JMS API的重要接口
ConnectionFactory :连接工厂,用于创建到特定JMS服务提供者(如Weblogic,IBM MQ或是Sonic MQ)的消息服务的连接。可以用JNDI查询到一个JMS服务提供者管理的初始ConnectionFactory对象的句柄。它提供了返回特定Connection对象实例的方法,可以使用ConnectionFactory.createConnection()得到Connection对象的句柄。
Connection :它封装了一个从JMS 客户端到JMS服务提供者(JMS Provider)实例的连接 。
Destination :消息的目的地
Session:JMS会话,它与Connection相关联,便是创建消息的上下文。会话可以用于定义一个事务,在该事务的边界中可以存在一组在十五中发送和接收的消息,因此,所有这些消息可包含在一个原子事务中。Session接口封装了一个上下文,JMS消息是在这个上下文中创建和接收的。它还扩展了java.lang.Runnable接口,表明每一个会话运行在单个线程的上下文中,可以用Connection.createSession()方法创建Session对象的句柄,传递给方法的Boolean参数表明会话是否是事务性的。附加在会话上的监听器也可用于得到更多异步回调行为。MessageListener实现了一个onmessage()方法,对于所有由这个Session对象收到的消息,它取Message作为参数,Session对象的setMessageListener和getMessageListenner分别设置和取得MessageListener。
MessageProducer: 由Session 对象创建的用来发送消息的对象
MessageConsumer: 由Session 对象创建的用来接收消息的对象

开发JMS客户端的几个步骤:
广义上说,一个JMS应用是几个JMS 客户端交换消息,开发JMS客户端应用由以下几步构成:
1) 用JNDI 得到ConnectionFactory对象;
2) 用JNDI 得到目标队列或主题对象,即Destination对象;
3) 用ConnectionFactory创建Connection 对象;
4) 用Connection对象创建一个或多个JMS Session;
5) 用Session 和Destination 创建MessageProducer和MessageConsumer;
6) 通知Connection 开始传递消息。】

JMS消息模型
JMS 消息由以下几部分组成:消息头,属性,消息体。
1.消息头(header):JMS消息头包含了许多字段,它们是消息发送后由JMS提供者或消息发送者产生,用来表示消息、设置优先权和失效时间等等,并且为消息确定路由。
2.属性(property):由消息发送者产生,用来添加删除消息头以外的附加信息。
3.消息体(body):由消息发送者产生,JMS中定义了5种消息体:ByteMessage、MapMessage、ObjectMessage、StreamMessage和TextMessage。

JMS的五种专用消息类型
JMS API中定义了五种类型的消息,它们扩展了Message接口并对应于五种类型的消息正文数据。它们是:
1.TextMessage:正文为基础java.lang.String对象的消息,如xml文件内容。
2.MapMessage:正文为底层键值对集合的消息,键是String对象,值类型可以是Java任何基本类型。
3.BytesMessage:正文为字节集合。
4.StreamMessage:正文为Java中的输入输出流。
5.ObjectMessage:正文为Java中的可序列化对象(实现Serializable接口的对象)。

当前比较流行的JMS商业软件和开源产品:
目前许多厂商采用并实现了JMS API,现在,JMS产品能够为企业提供一套完整的消息传递功能,下面是一些比较流行的JMS商业软件和开源产品。
1.IBM MQSeries
IBM MQ系列产品提供的服务使得应用程序可以使用消息队列进行相互交流,通过一系列基于Java的API,提供了MQSeries在Java中应用开发的方法。它支持点到点和发布/订阅两种消息模式,在基本消息服务的基础上增加了结构化消息类,通过工作单元提供数据整合等内容。

2.WebLogic
WebLogic是BEA公司实现的基于工业标准的J2EE应用服务器,支持大多数企业级JavaAPI,它完全兼容JMS规范,支持点到点和发布/订阅消息模式,它具有以下一些特点:
1) 通过使用管理控制台设置JMS配置信息;
2) 支持消息的多点广播;
3) 支持持久消息存储的文件和数据库;
4) 支持XML消息,动态创建持久队列和主题。

3.SonicMQ
SonicMQ是Progress公司实现的JMS产品。除了提供基本的消息驱动服务之外,SonicMQ也提供了很多额外的企业级应用开发工具包,它具有以下一些基本特征:
1) 提供JMS规范的完全实现,支持点到点消息模式和发布/订阅消息模式;
2) 支持层次安全管理;
3) 确保消息在Internet上的持久发送;
4) 动态路由构架(DRA)使企业能够通过单个消息服务器动态的交换消息;
5) 支持消息服务器的集群。

4.Active MQ
Active MQ是一个基于Apcache 2.0 licenced发布,开放源码的JMS产品。其特点为:
1) 提供点到点消息模式和发布/订阅消息模式;
2) 支持JBoss、Geronimo等开源应用服务器,支持Spring框架的消息驱动;
3) 新增了一个P2P传输层,可以用于创建可靠的P2P JMS网络连接;
4) 拥有消息持久化、事务、集群支持等JMS基础设施服务。

5.OpenJMS
OpenJMS是一个开源的JMS规范的实现,它包含以下几个特征:
1) 它支持点到点模型和发布/订阅模型;
2) 支持同步与异步消息发送;
3) 可视化管理界面,支持Applet;
4) 能够与Jakarta Tomcat这样的Servlet容器结合;
5) 支持RMI、TCP、HTTP与SSL协议。

Weblogic9.2中JMS的相关配置
http://www.blogjava.net/heyang/archive/2009/09/24/296244.html

参考程序
http://www.blogjava.net/Files/heyang/WeblogicStandardJMS_Queue20090924101108.rar
http://www.blogjava.net/Files/heyang/WeblogicStandardJMS_Topic20090924101059.rar
http://www.blogjava.net/Files/heyang/SpringJMS_Queue20090924103214.rar
http://www.blogjava.net/Files/heyang/SpringJMS_Topic20090924103204.rar
posted @ 2009-09-24 11:04 何杨| 编辑 收藏

     摘要: 使用Spring的MDP,我们可以与JMS服务提供者通信,向队列或是主题发送或消费JMS消息,下面是两个示例程序,分别是与队列和主题通信,两个程序大同小异,主要是配置上有些许区别,其中Weblogic中的JMS相关配置部分请见“Weblogic9.2中JMS的相关配置”。 为了减少体积,这两个程序去除了库,这些库是commons-logging-1.0.4.jar,d...  阅读全文
posted @ 2009-09-24 10:45 何杨| 编辑 收藏

一.创建JMS服务器
JMS服务器是Weblogic之下的一个JMS消息处理器,我们需要首先配置好一个JMS服务器.在后面的队列或是主题的配置中,需要用到它.

一.JMS服务器的配置
1.1 首先,点击"域结构"中的"服务"->"消息传递"->"JMS服务器",准备开始建立JMS服务器.当然,在创建之前,需要点击"更改中心"中的"锁定并编辑按钮".


1.2 输入JMS服务器的名称,暂时还不需要配置持久性存储.


1.3 选择要部署此 JMS 服务器的服务器实例或可迁移目标.


1.4 JMS服务器创建成功了,点击"更改中心"中的"激活更改"按钮,激活刚才进行的修改.


1.5 激活完毕后的画面.


二.创建JMS模块.

JMS模块是JMS连接工厂ConnectionFactory,队列Queue和主题Topic的载体,因此我们在建立它们之前需要建立一个JMS模块.

2.1 点击域结构下的"服务"->"消息传递"->"JMS模块"链接,在点击"更改中心"中的"锁定并编辑"按钮,之后点击右侧的"新建"按钮,准备开始建立一个JMS模块.


2.2 输入JMS模块的名称.


2.3 选择要部署此 JMS 系统模块的服务器或群集


2.4 创建完成.


2.5 激活所进行的修改.至此,JMS模块创建完成.


三.创建JMS队列(Queue)
JMS模型有两种,一种是队列Queue,它的最主要特征是一条JMS消息只会被一个JMS消息消费者接收;另一种是主题Topic,它的最主要特征是一条JMS消息会被订阅了此主题的所有订阅者接收.我们先来看看队列的配置.

3.1 点击刚才创建好的JMS模块"MyJMSModule"链接,准备在它底下建立一个队列.


3.2 点下"锁定并编辑"按钮,点击右边的"新建"按钮,准备新建一种资源.


3.3 这个界面中列出了多种资源类型,我们现在要建立的队列,当然点击"队列"单选框了.


3.4 输入队列的JNDI名,这个很重要,队列的发送者和消费者要找到队列就要靠它.而名称可以随便写,采取默认也行.写完后点击"下一步"按钮.


3.5 点击此画面中的"新建子部署"按钮,准备新建一个子部署.


3.6 点击确定按钮,子部署名可以采用默认值.


3.7 为队列指定子部署和JMS服务器,这两个就是我们刚才和一开头配置的。之后点击“完成”按钮。


3.8 至此,JMS队列创建成功,在"更改中心"中,点击"激活更改"按钮,激活所进行的更改.


四.建立JMS主题.

4.1 JMS主题和JMS队列,JMS连接工厂都是JMS模块下的一种资源,同样,建立一个JMS主题前,仍然是点击JMS模块链接,然后也选择新建一种资源,进入如下页面后,点击"主题"单选框.


4.2 输入JMS主题的JNDI名,这很重要,主题的发布者和订阅者找到它就靠这个名,而名称则可以随意.之后点击"下一步"按钮.


4.3 进入此页面,为主题选择子部署和JMS服务器,之前,我们在建立JMS队列的时候也进行同样的配置。

4.4 JMS主题创建成功,激活所进行的修改。



五.建立连接工厂ConnectionFactory.
我们单有队列或是主题是不够的,还需要建立一个连接工厂,连接工厂用于创建到特定JMS服务提供者的消息服务的连接,它常与队列或是主题配合使用.

5.1 连接工厂也是JMS模块下的一种资源,我们同样是和前面一样,点击JMS模块链接,再选择新建按钮,进去下面的页面后,选择"连接工厂"单选框.


5.2 输入连接工厂的JNDI名,而名称则可以随意.


5.3 进入此页面后,点击完成按钮,至此连接工厂配置完成.之后选择激活所进行的更改即可.


最后,提供两个程序给大家测试一下刚才配置的正确性.
http://www.blogjava.net/Files/heyang/WeblogicStandardJMS_Topic20090924101059.rar
http://www.blogjava.net/Files/heyang/WeblogicStandardJMS_Queue20090924101108.rar

注意为了减少体积,weblogic.jar已经删除,大家请自行添加进去,另这两个程序中的队列或主题和连接工厂和上面配置的可能有大小写的不同,请大家注意修改.
posted @ 2009-09-24 10:16 何杨| 编辑 收藏

使用Ajax从后台取得反馈信息后总要在前台显示给客户看,显示方式从简单到复杂一般有三种:1.使用alert显示文字,这种方式简便易用,但容易中断用户操作,效果也太死板;2.改变页面某一区域的文字或是图片,这种使用起来也比较方便,实现也不复杂;3.使用新窗口显示,这种效果最好,但实现复杂些。本例中采用第二种方式。

Ajax提示信息出现的位置在于次级菜单栏的下方,边栏和内容栏的上方,由两部分组成:图标和文字。


消息栏的HTML代码如下:
<div id="msgDiv">
    
<span id="iconSpan">
        
<img src="web/img/icon/ok.gif" width="24px" height="24px"/>
    
</span>
    
<span id="msgSpan">
        fdsfdsfsdfsdfsdfsdfsdfsdfsdfsd
    
</span>
</div>

定义它们的CSS如下:
#msgDiv{
    display
:none;
}
#iconSpan
{
    vertical-align
:middle;
}
#msgSpan
{
    height
:100%;
    font-size
:16px;         
    color
:#404040;     
}

在没有Ajax消息前,它们整体是不表现的,通过display:none进行限制;有消息发生后,再使用JavaScript改变msgDiv,iconSpan,msgSpan的状态和内容即可,基本原理很简单,但我们不希望把代码弄乱,因此需要用类整合一下。

其中iconSpan中将显示的图标共有四种:加载图标,用于在从服务器取回响应前;完成图标,用于从服务器取得正确信息后;警告图标,用于从服务器取得错误信息后;错误图标,用于无法取得来自服务器的响应时。这样,用户在仔细查看文字前,就能大致了解发生的情况,为了使用上的方便,我特地把Ajax消息显示器组合成了一个Msger类。如下所示:
/*************************
*
*   Class:Msger
*   2009-9-9
*************************
*/
//-- Contructor
function Msger(){
    
this.msgDiv=$("msgDiv");
    
this.iconSpan=$("iconSpan");
    
this.msgSpan=$("msgSpan");
    // 这里是四种图标
    
this.icons=new Array;
    
this.icons[0]="<img src='web/img/icon/error.gif' width='24px' height='24px'/>";
    
this.icons[1]="<img src='web/img/icon/loading.gif' width='24px' height='24px'/>";
    
this.icons[2]="<img src='web/img/icon/ok.gif' width='24px' height='24px'/>";
    
this.icons[3]="<img src='web/img/icon/warning.gif' width='24px' height='24px'/>";
    
    
this.timer=new Object;
}
// 显示错误信息,出现后不消失
Msger.prototype.showErrorMsg
=function(msg){
    
this.msgDiv.style.display="block";
    
this.iconSpan.innerHTML=this.icons[0];
    
this.msgSpan.innerHTML=msg;
    
this.msgSpan.style.color="#ff0000";
}
// 显示载入信息,出现后不消失,因为很快会被其他信息替代
Msger.prototype.showLoadingMsg
=function(msg){
    
this.msgDiv.style.display="block";
    
this.iconSpan.innerHTML=this.icons[1];
    
this.msgSpan.innerHTML=msg;
    
this.msgSpan.style.color="#404040";
}
// 显示正确消息,使用后渐渐消失
Msger.prototype.showOkMsg
=function(msg){
    
this.msgDiv.style.display="block";
    
this.iconSpan.innerHTML=this.icons[2];
    
this.msgSpan.innerHTML=msg;
    
this.msgSpan.style.color="#404040";
        
    
this.timer=setTimeout("msger.fadeout()",2000);
}
// 显示警告消息,出现一段时间后消失
Msger.prototype.showWarningMsg
=function(msg){
    
this.msgDiv.style.display="block";
    
this.iconSpan.innerHTML=this.icons[3];
    
this.msgSpan.innerHTML=msg;
    
    
this.msgSpan.style.color="#f5692e";
    
    
this.timer=setTimeout("msger.hide()",5000);
}
// 隐藏
Msger.prototype.hide
=function(){
    
this.msgDiv.style.display="none";
    clearTimeout(
this.timer);
}
// 渐渐消失
Msger.prototype.fadeout
=function(){
    
var colorRGB=this.msgSpan.style.color;
    
var color=parseInt(colorRGB.slice(1,3),16)+3;
    
    
if(color<256){
        
var v1=(Math.floor(color/16)).toString(16);
        
var v2=(Math.floor(color%16)).toString(16);
        
var colorStr="#"+v1+""+v2+v1+""+v2+v1+""+v2;
        
this.msgSpan.style.color=colorStr;
        
        
this.timer=setTimeout("msger.fadeout()",120);
    }
    
else{
        
this.hide();
    }        
}

上面这些函数都好理解,fadeout函数还需要赘述一下,让文字渐渐消失是通过修改文字的颜色实现的,使它不断向纯白色靠拢就行,另外使用setTimeout调用自身的写法“msger.fadeout()”要值得注意。以上函数大家务必要理解。

使用上就比以前简化了,以下是用户列表页面的例子:
<script language="javascript">
<!--
//-- 消息显示器
var msger;

/*****************************************************
* 窗口载入时调用的启动函数
****************************************************
*/
window.onload
=function(){
    .
    
    
// 初始化消息显示器
    msger=new Msger;
    
    .
}


/*****************************************************
* 选择成员加入Session
****************************************************
*/
function selectMember(id){
    msger.showLoadingMsg(
"将选择的用户id'"+id+"'加入项目备选用户名单中");
    
    
new Ajax.Request(prjName+'SelectUsersIntoSession.do?id='+id,
           {     
               method:'get',     
               onSuccess: 
function(ajaxObj){                            
                    
var status=ajaxObj.responseXML.getElementsByTagName("status")[0].firstChild.data;
                    
// alert(ajaxObj.responseText);
                    
                    
if(status=="ok"){    
                        
var text=ajaxObj.responseXML.getElementsByTagName("text")[0].firstChild.data;
                        msger.showOkMsg(text);
                    }
                    
else{
                        
// 返回错误信息
                        var text=ajaxObj.responseXML.getElementsByTagName("text")[0].firstChild.data;
                        msger.showWarningMsg(text);
                    }
               },     
               onFailure: 
function(){ 
                   msger.showErrorMsg(
"无法取得服务器的响应");
               }   
            }
          );  
}
//-->
</script>

其中,类实例msger与前面的“this.timer=setTimeout("msger.fadeout()",120);”中的msger是呼应的,需要注意。

好了,就到这里,全体代码请到“

ProjectManager框架下载(更新时间2009年9月10日14:59:48)下载。




--全文完--



posted @ 2009-09-10 14:46 何杨| 编辑 收藏

在ProjectManager中,由于用户需要进行的操作较多,于是采用了主菜单和次级菜单的形式来扩大菜单容量。但新问题是用户不容易记住当前所处的位置,对于新手尤其是这样,因此,在主菜单和次级菜单上把当前位置标出是对用户有所帮助的,具体形式如下图所示:

具体怎么做到这一点呢?使用JS分别把主菜单和次级菜单的当前项更换一个类别就可以,首先我们看看两个菜单css定义:
#menubar{
    width
: 950px;
    height
: 30px;
    margin-left
:auto;
    margin-right
:auto;
}
#menubar ul
{
    margin
:0px;
    padding
:0px;
    list-style-type
:none;
}
#menubar li
{
    float
:left;
    display
:block;    
    height
:30px;
    line-height
:30px;
}
#menubar li.leftBlank
{
    width
:235px;
    text-align
:left; 
    font-size
:20px;         
    color
:#000000;      
}
#menubar li.rightBlank
{
    width
:235px;
    text-align
:right;   
  
}
#menubar li a
{
    width
:96px;
    
    
    font-size
:16px;         
    color
:#404040;
    text-decoration
:none;
    text-align
:center;        
    background
:#ffffff url(../img/manubar.gif) 0px 0px;
}

#menubar li a.current
{
    width
:96px;
    color
:#ffffff;
    font-weight
:bold;
    background
:#ffffff url(../img/manubar.gif) 0px -49px;
    border
:0px;
}


#submenubar
{
    width
: 950px;
    height
: 31px;
    margin-left
:auto;
    margin-right
:auto;
    background
:#000000 url(../img/submanubar.gif) 0px -1px repeat-x;
    border-left
:1px #ff7101 solid;
    border-right
:1px #ff7101 solid;
}

#submenubar ul
{
    margin
:0px;
    padding
:0px;
    list-style-type
:none;
}
#submenubar li
{
    float
:left;
    height
:31px;
    line-height
:31px;
}
#submenubar li a
{    
    padding-left
:20px;
    font-size
:12px;         
    color
:#ffffff;
    text-decoration
:none;
    text-align
:center;        
}

#submenubar li a.current
{       
    color
:#c20002;
    font-weight
:bold;          
}


#submenubar li a:hover
{
    text-decoration
:underline;
}

上面加粗的部分,就是我们要用JS赋给当前菜单项的,这就比较简单了,找出来修改className即可,代码如下:
/*****************************************************
* 设置顶级菜单中当前页所处的位置
****************************************************
*/
function setCurrentMenu(menuNumber){
    
// 设置主菜单
    var manubar=$("menubar");
    
var menu=manubar.childNodes[2].childNodes[menuNumber].firstChild;
    menu.className
="current";
}

/*****************************************************
* 设置次级菜单中当前页所处的位置
****************************************************
*/
function setCurrentSubMenu(menuNumber){
    
// 设置次级菜单
    var manubar=$("submenubar");
    
var menu=manubar.childNodes[2].childNodes[menuNumber].firstChild;
    menu.className
="current";
}

其后,在页面的窗体载入事件中调用这两个函数即可,指定menuNumber即可指定当前菜单项。userMenuIntroBody.jsp中示例调用如下:
<script language="javascript">
<!--

/*****************************************************
* 窗口载入时调用的启动函数
****************************************************
*/
window.onload
=function(){
    
// 设置当前页在主菜单和次级菜单中的位置
    setCurrentMenu(1);
    setCurrentSubMenu(
5);
    
    
// 隐藏边栏,加宽内容栏,使得内容如同全屏一样
    makeConceptFullScreen();
}
//-->
</script>
这样,就做到了是第一个主菜单,第五个次级菜单变成当前项,分别用黄色条纹图片背景和红色加粗字体标识出来。

有两点需要赘述一下,一是主菜单的第一项是第二个li节点,第一个是左空白,而次级菜单的第一项是第一个li节点;二是原始的菜单项都是没有指定current类别的,都是在具体页面中用JS指定。具体大家多看看代码。

--全文完--

posted @ 2009-09-10 10:57 何杨| 编辑 收藏

仅列出标题
共28页: First 上一页 18 19 20 21 22 23 24 25 26 下一页 Last