`

用dom4j实现openfire式导航菜单

阅读更多

 

 借鉴Openfire项目中菜单设计的优点,结合我们自己的需求,通过一个小实例,来说明一下如何使用dom4j来实现这个功能。
    可以在http://www.igniterealtime.org/downloads/index.jsp 这里下载到Openfire的发布版和源码版,Openfire的介绍在这里不作描述,网上相关的资料有很多,有兴趣的朋友可以下载源码研究一下,其实不了解Openfire也没关系,因为我们只是借鉴其中的一些思想,等做完了实例,就会有一个比较直观的认识。
    大象建议先在最后面下载源码和必要的JAR包,让程序运行起来看下效果,再看下面的详细说明。
    开发环境:Eclipse 3.2.1  MyEclipse 5.10GA  Tomcat 6.10  
             dom4j-1.6.1.jar  jaxen-1.1-beta-7.jar  sitemesh-2.2.1.jar

    1、创建tag-console.xml
       在src目录下建一个tag-console.xml文件,这个配置文件中写的是菜单信息,内容如下:

<?xml version="1.0" encoding="GBK"?>
<bookstore>
    
<global>
        
<appname>菜单导航demo</appname>
        
<version>ver 1.0</version>
        
<creator>菠萝大象</creator>
    
</global>
    
<catalog id="catalog-program" name="编程开发" url="index.jsp" description="编程开发">    
        
<item id="item-program-java" name="Java开发" url="index.jsp" description="Java开发">

            
<book id="695043" name="Struts2 深入详解" url="index.jsp" description="Struts2 深入详解"/>
            
<book id="691254" name="Ant整合开发" url="book_ant.jsp" description="Ant整合开发"/>
            
<book id="693668" name="Java编程思想" url="book_java.jsp" description="Java编程思想"/>
        
</item>
        
<item id="item-program-database" name="数据库开发" url="book_oracle9.jsp" description="数据库开发">
            
<book id="691245" name="Oracle 9i数据库宝典" url="book_oracle9.jsp" description="Oracle 9i数据库宝典"/>
            
<book id="693254" name="SQL Server 2005应用开发" url="book_sqlserver.jsp" description="SQL Server 2005应用开发"/>
            
<book id="690215" name="Oracle 10g高级开发" url="book_oracle10.jsp" description="Oracle 10g高级开发"/>
        
</item>
    
</catalog>
    
<catalog id="catalog-system" name="系统相关" url="book_vista.jsp" description="系统相关">
        
<item id="item-system-windows" name="Windows系统" url="book_vista.jsp" description="Windows系统">
            
<book id="691258" name="Windows Vista入门" url="book_vista.jsp" description="Windows Vista入门"/>
            
<book id="695489" name="Windows注册表实战" url="book_windows.jsp" description="Windows注册表实战"/>
        
</item>
        
<item id="item-system-linux" name="Linux系统" url="book_linux9.jsp" description="Linux系统">
            
<book id="696598" name="Linux 9.0详解" url="book_linux9.jsp" description="Linux 9.0详解"/>
            
<book id="694585" name="Linux宝典" url="book_linux.jsp" description="Linux宝典"/>
        
</item>
    
</catalog>
</bookstore>

       上面XML里面的东西我是随便写的,大家千万不要较真,我用图书来做菜单一是方便大家理解,另一个是简化程序,其实Openfire的服务器端是一个后台管理系统,它是基于XMPP(可扩展消息处理现场协议)开发的,XMPP贯穿整个系统设计,如果你想用它的控制台框架,但又不想用XMPP,请先从网页入口开始,结合页面仔细分析代码,把需要的部分抽取出来就行了,其它的不用去管。大象没有研究过XMPP,只是抽取了控制台框架,对Openfire的源代码也没能深入的研究,最主要还是E文太烂了。^_^
       Openfire没有采用现在很流行的技术架构(SSH),只使用JSP+JavaBean,但是它有自己的系统设计,就连日志都是自己做的,没有使用我们熟悉的log4j,真的是太佩服鸟~~~~
    
2、创建ResourceManage.java
       在util包下创建ResourceManage类,这个类主要是用来读取tag-console.xml文件,并取得文件中的基本信息,以及查找元素等操作。
       我们先在Constant接口中,增加一个字符串常量:String TAG_CONFIG = "tag-console.xml"
       ResourceManage前面加载资源的部分和上一篇,后来修改过的DataBaseConnect类一样,只需把Constant.DB_CONFIG换成Constant.TAG_CONFIG就行了。
接下来,在类中加入几个读取XML中基本信息的方法:

    <global>
        
<appname>菜单导航demo</appname>
        
<version>ver 1.0</version>
        
<creator>菠萝大象</creator>
    
</global>

       这里只举出取得appname元素值的方法,其它的几个都很相似,请查看源代码。

    /**
     * 得到应用程序名称
     
*/
    
public static String getAppName(){
        Element appName 
= (Element) coreModel.selectSingleNode("//bookstore/global/appname"
);
        
if(appName!=null
){
            
return
 appName.getText();
        }
else
{
            
return null
;
        }
    }

       根据id属性值查找对应的元素:

    /**
     * 在整个文档节点中查找id属性值为传入id的元素对象
     * 
@param id 待查找的id属性值
     * 
@return
 返回找到的元素对象
     
*/

    
public static Element getSingleElementById(String id){
        
return (Element)coreModel.selectSingleNode("//*[@id='"+id+"']"
);
    }

       这里用到了XPATH语法,根据传入的id值,在整个文档中查找id属性值与此一致的元素对象。用下面的代码举例说明:

    <book id="695043" name="Struts2 深入详解" url="index.jsp" description="Struts2 深入详解"/>

       当传入的id属性值为"695043"时,那么我们就会得到对应这个id值的book元素对象,id属性值在整个配置文件中就是一个key关键字,起到定位的作用。
       根据id属性值查找上下文中对应的catalog元素

    /**
     * 根据传入的id查找上下文中对应的catalog元素
     * 
@param id 待查找的id属性值
     * 
@return
 返回id值所在的catalog元素对象
     
*/

    
public static Element getElementByID(String id) {
        
return (Element) coreModel.selectSingleNode("//*[@id='" + id
                + "']/ancestor::catalog");

    }

       ancestorXPATH语法中轴的概念,我引用网上官方文档中的说明:“ancestor(axis)包含上下节点的祖先节点,该祖先节点由其上下文节点的父节点以及父节点的父节点等等诸如此类的节点构成,所ancestor轴总是包含有根节点,除非上下文节点就是根节点本身。这句话的意思其实就是向上查找节点,直到找到根节点为止。对于ancestor::catalog来说,就是向上查找直到catalog节点为止。所以getElementByID这个方法是根据传入的id属性值在上下文中查找节点,直到找到这个id值所在的上下文catalog节点为止。当传入的id属性值为"695043"时,我们会得到id="catalog-program"这个catalog节点元素,而不会得到id="catalog-system"这个catalog节点元素。这样说大家大概能明白是什么意思了吧?
       可以去这个网站看下XPATH教程:http://www.zvon.org/xxl/XPathTutorial/General_chi/examples.html
    3、自定义标签  
       采用自定义标签的方式来生成菜单,借助ResourceManage类取出XML文件中的信息,将这些内容装载到标签体中,然后在JSP页面中呈现出来。
       1主菜单标签
          主菜单有两个,catalog元素这一层表示主菜单。标签的实现类如下:
          MainTag.java
          建com.demo.tag包,在tag包下创建MainTag类,继承javax.servlet.jsp.tagext.BodyTagSupport
类,主要的部分代码如下,完整代码请下载源码包查看。
          这些属性与demo.tld中的属性对应,每个属性都有settergetter方法。

    private String css; //菜单的CSS样式
    private String currentcss; //当前选中菜单的CSS样式

         doStartTag()方法是遇到标签开始时调用的方法,EVAL_BODY_BUFFERED表示创建一个缓冲流,将标签体的内容保存到BodyContent对象中,可以对其内容进行修改。BodyContent继承了javax.servlet.jsp.JspWriter类,BodyContent对象的内容不自动写入servlet的输出流,而是放在一个字符流缓存中。当标签体完成后其对象仍可在doEndTag()方法中应用,由getString()getReader()方法操作。并在必要时修改及写入恢复的JspWriter输出流。EVAL_BODY_INCLUDE表示将显示标签间的文字。另一个返回值是SKIP_BODY,它表示不显示标签间的文字。

    public int doStartTag() throws JspException {
        
return EVAL_BODY_BUFFERED; //创建保存到BodyContent对象中的缓冲流

    }

          doEndTag()方法是遇到标签结束时调用的方法,EVAL_PAGE表示处理完标签后继续执行标签之后的JSP页面。另一个返回值SKIP_PAGE表示不处理标签之后的JSP网页。

    public int doEndTag() throws JspException {
        
//代码主体省略,请查看源码

        return EVAL_PAGE; //处理完标签后继续执行标签之后的JSP页面
    }

          doEndTag()方法中部分比较重要的代码说明:
          使用pageContext对象在JSP页面上下文取得请求,不过请注意pageContext,它定义在javax.servlet.jsp.tagext.TagSupport中,而不是在BodyTagSupport中,因为BodyTagSupport继承了
TagSupport

    //使用pageContext对象在JSP页面上下文取得请求
    HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();

          取得请求中的pageID值,这个pageID值在每个jsp页面中放在meta标签中,通过sitemesh装饰器取出放到request中。

    /*
     * 从请求中得到pageID值,即每个JSP里meta的content值
     * 与XML文件中book元素的id属性值一致
     
*/
    String pageID 
= (String)request.getAttribute("pageID");

          将所有的catalog元素取出放到List集合中,这里是只取得catalog这一层级的元素,实质就是catalogs中只有两个对象,一个是id="catalog-program"另一个是id="catalog-system",使用dom4j我们会发现处理元素非常容易,API相当的丰富,想写成什么样完全凭你自己的想法。

    //将所有的catalog元素取出放到List集合
    List catalogs = ResourceManage.getCoreModel().selectNodes("//catalog");

          看看前面介绍的getElementByID这个方法,这个currentCatalog所表示就是pageID所在的catalog元素。上面的代码是为了和下面的代码结合来判断当前的菜单是否为选中,加入CSS样式显示。

    //pageID所在的catalog元素,主要用来判断当前菜单是否被选中
    Element currentCatalog = (Element)ResourceManage.getElementByID(pageID);

          从BodyContent中将标签体缓存流读取出来,标签在WebRoot/decorators/main.jsp<a href="[url]" title="[description]" onmouseover="self.status='[description]';return true;" onmouseout="self.status='';return true;">[name]</a>

    String value = getBodyContent().getString(); //得到标签体

          Catalogs里面是两个catalog对象,循环遍历取出,将标签体中的[id][url][name][description]替换为XML文件中的属性值,这样主菜单标签就生成了。

    for (int i=0; i<catalogs.size(); i++) {
        Element catalog 
= (Element)catalogs.get(i); //catalog元素对象

        String value = getBodyContent().getString(); //得到标签体
        /*
         * 将标签体中的[id]、[url]、[name]、[description]
         * 替换为XML文件中的属性值
         * attributeValue方法是取属性值
         
*/
        
if (value != null) {
            value 
= StringUtils.replace(value, "[url]"
, request
                       .getContextPath()
                       
+ "/" + catalog.attributeValue("url"
));            
            value 
= StringUtils.replace(value, "[name]",catalog.attributeValue("name"
));
            value 
= StringUtils.replace(value, "[description]"
,catalog.
                    attributeValue(
"description"
));
        }
        String css 
=
 getCss();
        
//对当前选中菜单添加CSS样式

        if (catalog.equals(currentCatalog)) {
            css 
=
 getCurrentcss();
        }
        buf.append(
"<li class=\"").append(css).append("\">"
);
        
if (i > 0
) {
            buf.append(
" | "
);
        }
        buf.append(value).append(
"</li>"
);
    }

       2、导航菜单及侧边栏菜单标签
           导航菜单每个catalog下都有两个,而侧边栏菜单则在item下定义,这两个标签类与主菜单的标签类没有太大的区别,主要就是生成标签体,匹配CSS样式,因此,代码中相同的部分我不再细述,只说一下不同的地方。
           在tag包下创建NavTag类和SideTag类,标签属性与MainTag一样,只是SideTag多了一个headercss属性,这是在页面显示时,加在边栏上当前选中项左侧小箭头的CSS
样式,不清楚的话,请运行程序后观察。
           NavTag.java
           根据pageID找到此元素对象:

    //根据pageID找到此元素对象,即book元素对象
    Element current = ResourceManage.getSingleElementById(pageID);

           如果current不为空,取得父节点,其为item元素。根据pageID值,如果为695043,则subnavid="item-program-java"item元素,如果为691245,则subnavid="item-program-database"item元素。这个subnav的作用也是用来判断当前的菜单是否为选中,加入CSS样式显示。

    Element subnav = null;
    
if (current != null
) {
        subnav 
= current.getParent(); //取得父节点,即item元素

    }

           SideTag.java
           在SideTag中也有上面的代码,但是subnav不再与CSS有关,而是取得它的所有子元素集合,即book元素集合,然后遍历所有book节点,取出属性值放入标签体中再输出到页面。
           我注释写得很详细,请查看代码了解细节。
    
4、创建StringUtils.java
       在util包下创建StringUtils类,这个类作为字符串处理类。添加public static String replace(String string, String oldString, String newString)方法,它的作用就是将标签体中的[id][url][name][description]替换为XML文件中的属性值。如果被替换的字符串在标签体中有多个,也能将它全部替换。

    /**
     * 将string中的oldString全部替换为newString
     * 
@param string 原始字符串
     * 
@param
 oldString 被替换的字符串
     * 
@param
 newString 要替换的字符串
     * 
@return
 返回替换完后的新string
     
*/

    
public static String replace(String string, String oldString, String newString) {
        
if (string == null
) {
            
return null
;
        }
        
int i = 0
;
        
//判断string中是否有被替换的字符串,i其实是索引值

        if ((i = string.indexOf(oldString, i)) >= 0) {
            
char[] string2 = string.toCharArray(); //字符串放入数组

            char[] newString2 = newString.toCharArray(); //要替换的字符串
            int oLength = oldString.length(); //被替换的字符串的长度
            StringBuilder buf = new StringBuilder(string2.length);
            
/*

             * 从索引0开始,按i值的长度在string2数组中截取字符
             * 将截取的字符放到buf中,接着再加入要替换的内容
             
*/
            buf.append(string2, 
0, i).append(newString2);
            i 
+=
分享到:
评论
1 楼 jimoshutong 2010-05-18  
请问有没有从数据库读出数据生成多级xml文件的例子啊

相关推荐

    dom4j-1.6.1.jar包和源码.zip

    dom4j.jar v1.6.1 和对应源码src.zip,用于 xml、openfire、xmpp 等 xml 解析,效率高、稳定

    Openfire下实现WebServer

    4. **文件上传与下载**:在Openfire的WebServer上实现文件上传和下载功能,可以为用户提供方便。这通常涉及到处理HTTP的POST请求来接收文件,以及提供GET请求来下载文件。Openfire的插件系统可以方便地扩展这些功能...

    Openfire安装配置手册

    使用Ant编译Openfire源代码,添加build.xml文件,编译源代码。 四、 修改Openfire源代码 修改org.jivesoftware.openfire.XMPPServer类的locateOpenfire()方法,将verifyHome("..", jiveConfigName)修改为...

    Android Openfire 登陆 注册实现代码

    在Android平台上实现Openfire的登录和注册功能,通常涉及到XMPP(Extensible Messaging and Presence Protocol)协议的使用,因为Openfire服务器就是基于XMPP的一种实时通信服务器。XMPP是一种开放的标准,用于即时...

    用flash+xiff+openfire 实现网页端接收推送消息

    标题中的“用flash+xiff+openfire 实现网页端接收推送消息”指的是一种技术集成方案,用于在网页上实现实时的消息推送功能。这里的关键技术包括Flash、XIFF库和Openfire服务器。 1. Flash:Flash是一款由Adobe公司...

    openfire 聊天实现

    【标题】"openfire 聊天实现"指的是利用Openfire服务器来构建一个类似微信的聊天应用程序。Openfire是一款开源的、基于XMPP(Extensible Messaging and Presence Protocol)协议的即时通讯服务器,它允许开发者创建...

    Android-XMPP-openfire-实现IM

    - **Smack库**:在Android中,开发者通常使用Smack库来实现XMPP协议,它提供了API用于连接Openfire服务器,发送和接收消息。 - **连接管理**:客户端需要建立与Openfire服务器的连接,处理登录、心跳保活、断线重...

    openfire的Android客户端实现

    openfire是xmpp协议的实现,以其及时性和稳定性被倍受青睐,在此贡献出来Android客户端,希望与大家共同探讨。 文章地址:http://blog.csdn.net/sky_monkey/article/details/9495571

    openfire_4_0_1

    在Windows环境下部署Openfire,可以实现高效、安全的实时通信功能,例如聊天、群组讨论、文件传输等。下面我们将深入探讨Openfire的核心特性和在Windows上的安装与配置过程。 1. **Openfire的主要特性** - **跨...

    基于openfire 仿QQ

    【基于Openfire 仿QQ】项目是一个实现类似QQ即时通讯功能的应用,利用开源的Openfire服务器作为后台支撑。Openfire是一款用Java编写的轻量级XMPP(Extensible Messaging and Presence Protocol)服务器,广泛用于...

    openfire深入浅出

    《OpenFire深入浅出》这本书是关于开源即时通讯服务器OpenFire的权威指南,它涵盖了OpenFire的各个方面,从基础安装到高级配置,旨在帮助读者全面理解并熟练掌握OpenFire的使用和管理。OpenFire是一款基于Java开发的...

    使用openfire登录android

    标题“使用openfire登录android”涉及的技术点主要是集成Openfire服务器和MySQL数据库,以及在Android平台上实现登录功能。Openfire是一款开源的即时通讯(Instant Messaging, IM)服务器,它基于XMPP(Extensible ...

    openfire_4_1_4.tar.gz

    4. **多语言支持**:Openfire的Web管理界面支持多种语言,便于全球用户使用。4.1.4版本可能对语言包进行了更新,优化了用户体验。 5. **插件系统**:Openfire拥有强大的插件生态系统,允许用户根据需求添加额外的...

    openfire+asmock实现android消息推送

    【标题】"openfire+asmock实现android消息推送"揭示了如何在Android平台上利用Openfire服务器和Mocking框架AsMock来构建一个实时的消息推送系统。Openfire是一款开源的即时通讯服务器,它基于XMPP(Extensible ...

    openfire4.2.3源码

    9. **日志和监控**:Openfire使用Log4j进行日志记录,并且有监控服务器性能的机制。源码中会有相关配置和实现。 10. **国际化支持**:Openfire支持多语言,源码中会有语言资源文件和本地化处理逻辑。 通过深入研究...

    openfire 3.9.3第二次开发集成所需要的包

    4. **PGP支持**:对于需要实现端到端加密的IM应用,Openfire可以通过Bouncy Castle库支持PGP协议,确保用户之间的私人对话不被中间人截取。 5. **XMPP扩展**:Openfire允许开发者添加自定义的XMPP扩展,如OMEMO加密...

    openfire使用hazelCast集群

    本文将从集群概念、两大关键特性、两大能力、两大技术实现四个方面介绍 OpenFire 使用 HazelCast 集群的知识点。 一、集群概念 集群是一组协同工作的服务实体,用以提供比单一服务实体更具扩展性与可用性的服务...

    openfire的相关资源

    Openfire是一款基于Java的开源即时通讯(IM)服务器,它提供了强大的实时通信功能,支持XMPP协议,可以用于构建企业级的聊天、协作系统。在本文中,我们将深入探讨Openfire的相关资源,包括如何搭建、源码配置、编译...

    openfire部署学习资料

    四、Openfire的集群与高可用性 1. 集群部署:通过集群部署,可以实现Openfire的负载均衡和高可用性,提高服务的稳定性和性能。 2. 数据库复制:在集群环境中,数据库的同步和一致性至关重要,了解并配置数据库复制...

Global site tag (gtag.js) - Google Analytics