一、开发之前

前面一篇文章我们介绍了有关Java web service的基础知识,这里我们使用Java web service 来开发我们的一个实例:计数器。在很多地方我们都可以看到这样的计数器,像网站的访问统计等。好了,不多说了。开始我们的Java web service 实例之旅:开发一个计数器。

网上关于WebService的例子,多是一些简单得不能再简单的hello world的例子,简单得以至于你对WebService没有什么感觉。有了WebService的基本知识后,我一直再想,可以把什么样的即简单又实用的东东做成WebService,开展示一下其功能特点。今天终于想到一个:计数器,计数器用得比较多,功能比较独立,做成WebService可以一劳永逸为不同系统(不管是JSP还ASP,这是WebService的突出优点)、不同应用节省此类编码工作,可以说以后再也不用写计数器了。只要用就行了。

二、基础工作

1.开发环境

我使用axis做为Web Service引擎,它是Apache的一个开源web service引擎。它目前最为成熟的开源web service引擎之一。下面我主要介绍一下如何使用Axis搭建web service 服务的环境。
①安装tomcat5.0应用服务器(也可以装5.5,不过我一直在用5.0);
②解压下载( http://ws.apache.org/Axis)后的axis包,将包中axis目录复制到tomcat目录下的webapps目录下; 
③将axis/WEB-INF/lib目录下类文件复制到tomcat目录下的common/lib目录下;
④重新启动tomcat,访问http://localhost:8080/axis/happyaxis.jsp,如果能访问,表示安装成功;
注意,axis有几个可选的包,如email.jar....,你可以找来放到tomcat目录下的common/lib目录下,如果不使用相关的功能也可以不用。
这样,开发环境就搭建好了。

2.如何部署Web Service

部署有三种方式:Dynamic Invocation Interface(DII)、Stubs方式、Dynamic Proxy方式;这里就介绍一下简单,也是我使用的方式:DII。
DII方式中,先写好服务的JAVA文件(假设名字为helloworld.java),然后把它(注意是源文件)拷贝到webapps/axis目录中,后缀改成jws(此时文件名为:helloworld.jws),然后访问连接http://localhost: 8080/Axis/helloworld.jws?wsdl,页面显示Axis自动生成的wsdl,这样一个Web Service就部署好了。怎么样,是不是很简。
我的计数器服务就是以这种方式部署的,下文中我会只说将计数器服务部署好,你可不要说:怎么部署,我不会呀。那在古代就要被打手板了。所以我想,古代只学四书五经也是件好事呀。现在要学这么多东西,半天学不会,手要被打烂了。 三、计数器服务的编写

计数器大家都知道了,比较简单。我的计数器也同样简单,有以下功能及特点:提供四种计数器(总数器、月计数器、周计数器及日计数器);考虑到Web Service要服务于多种应用,这个计数器还支持多个用户;使用XML文件来记录数据。
记录文件名为:d:"counter.xml。注意,此文件在服务里是硬编码,如果修改名字,请在服务程序中也进行相应的修改。文件内容格式如下:

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

<counter> 

<item> 

<name>wallimn</name> 

<password>123</password> 

<dc>59</dc> 

<wc>59</wc> 

<mc>59</mc> 

<tc>59</tc> 

<rt>2007-4-16 16:01:29</rt> 

</item> 

<counter>


说是计数器服务,其实跟编写普通的JAVA应用没有什么两样。我的计数器代码比较简单,我就不做过多的介绍了,把它贴在下面,源码中有少量注释,相信大家不看注释也看得懂。服务有只有一个接口:Counter(String name, String password),以后在客户端拿来用就可以了。还多说一句,我和程序使用到了dom4j解析xml包,要调试的请自行准备好jar包。部署方法请参照上文。

import java.io.File; 

import java.io.FileWriter; 

import java.io.IOException; 

import java.text.DateFormat; 

import java.text.ParseException; 

import java.util.Calendar; 

import java.util.Date; 

import java.util.Iterator; 

import org.dom4j.Document; 

import org.dom4j.DocumentException; 

import org.dom4j.Element; 

import org.dom4j.io.SAXReader; 

import org.dom4j.io.XMLWriter; 


public class WsCounterByWallimn { 

//计数器文档,注意名字名路径。 

private final String FileN = "d:/counter.xml"; 

private final static DateFormat DATEFORMATER = DateFormat.getDateTimeInstance(); 

//出错的情况下返回的值 

private final static String ERRORINFO="-1;-1;-1;-1"; 

public WsCounterByWallimn(){ } 



/** 

*功能:打开计数器文档<br/> 

*/ 

private Document openDocument(){ 

Document doc=null; 

SAXReader reader = new SAXReader(); 

try { 

File xmlfile = new File(FileN); 

doc = reader.read(xmlfile); 

xmlfile=null; 

} 

catch (DocumentException e) { 

e.printStackTrace(); 

} 

return doc; 

} 



/** 

*功能:取指定名称计数器的详细信息,并设置计数器加1。<br/> 

*/ 

private synchronized String countertick( String name, String password){ 

Document doc = openDocument(); 

Date currdt = new java.util.Date(); 

//mc:month counter(月计数器); dc: day counter(日计数器); 

//tc: total counter(总计数器); wc: week counter(周计数器); 

//rt: registe time登记时间 

String mc="-1",dc="-1",tc="-1",wc="-1",rt="-1"; 

Element root = doc.getRootElement(); 

Element selitem=null,item=null; 

for(Iterator it = root.elementIterator("item"); it.hasNext();){ 

item = (Element)it.next(); 

if(name.equals(item.element("name").getText())){ 

selitem=item; 

String pwd = item.elementText("password"); 

if(!password.equals(pwd)){ 

return ERRORINFO;//密码不对,直接返回 

} 

mc=item.element("mc").getText(); 

dc=item.element("dc").getText(); 

tc=item.element("tc").getText(); 

wc=item.element("wc").getText(); 

rt=item.element("rt").getText(); 

break; 

} 

} 


//如果selitem为空,说明没有个名字的计数器。则添加一个。 

if(selitem==null){ 

//System.out.println("没有找到这个名字的计数器:"+name); 

rt=DATEFORMATER.format(currdt); 

selitem = doc.getRootElement().addElement("item"); 

selitem.addElement("name").setText(name); 

selitem.addElement("tc").setText("0"); 

selitem.addElement("mc").setText("0"); 

selitem.addElement("wc").setText("0"); 

selitem.addElement("dc").setText("0"); 

selitem.addElement("rt").setText(rt); 

selitem.addElement("password").setText(password); 

mc="0"; 

wc="0"; 

dc="0"; 

tc="0"; 

} 


//处理计数器加一操作。 

Calendar currcr=Calendar.getInstance(); 

//总数器总是加1。 

tc =String.valueOf(Integer.parseInt(tc)+1); 

selitem.element("tc").setText(tc); 

Date lastdt = null; 

try { 

lastdt = DATEFORMATER.parse(rt); 

} 

catch (ParseException e) { 

lastdt = new java.util.Date(); 

} 

Calendar lastcr = Calendar.getInstance(); 

lastcr.setTime(lastdt); 

currcr.setTime(currdt); 

//System.out.println("上次登记时间:"+DATEFORMATER.format(lastdt)); 

//System.out.println("本次登记时间:"+DATEFORMATER.format(currdt)); 

if(lastcr.get(Calendar.YEAR)==currcr.get(Calendar.YEAR)){ 

//月相同,月计数加1 

if(lastcr.get(Calendar.MONTH)==currcr.get(Calendar.MONTH)){ 

mc = String.valueOf(Integer.parseInt(mc)+1); 

} 

else{ 

mc="1"; 

} 

//日相同,日计数加1 

if(lastcr.get(Calendar.DAY_OF_YEAR)==currcr.get(Calendar.DAY_OF_YEAR)) 

dc = String.valueOf(Integer.parseInt(dc)+1); 

else 

dc = "1"; 


if(lastcr.get(Calendar.WEEK_OF_YEAR)==currcr.get(Calendar.WEEK_OF_YEAR)) 

wc = String.valueOf(Integer.parseInt(wc)+1); 

else 

wc = "1"; 

} 

else{//年不一样,则月计数器、周计数器日计数器肯定也不一样。 

mc="1"; dc="1"; wc="1"; 

} 

selitem.element("mc").setText(mc); 

selitem.element("wc").setText(wc); 

selitem.element("dc").setText(dc); 

//登记记录时间 

selitem.element("rt").setText(DATEFORMATER.format(currdt)); 


try { 

XMLWriter xw = new XMLWriter(new FileWriter(FileN)); 

xw.write(doc); 

xw.close(); 

} 

catch (IOException e) { 

e.printStackTrace(); 

} 

return tc+";"+mc+";"+wc+";"+dc; 

} 

/** 

*功能:服务暴露的接口,也就是指定名称、密码,返回指定的计数,并将计数器加1。<br/> 

*/ 

public String Counter(String name, String password){ 

if(password==null || name==null)return ERRORINFO; 

return countertick(name, password); 

} 

} 


四、客户端编写 


客户端是个页面,为了条理清晰,我先写个调用Web Service的类,其内容如下: 

package com.wallimn.WebService;//调试请注意包名 


import org.apache.axis.client.Call; 

import org.apache.axis.client.Service; 


public class CounterServiceClient { 

private String counterarr[]; 


public boolean getCounter(String CounterName, String password) { 

boolean res = false; 

try { 

String endpoint = "http://localhost:8080/axis/WsCounterByWallimn.jws";//此处注意,请与你的开发环境匹配 

Service service = new Service(); 

Call call = (Call) service.createCall(); 

call.setTargetEndpointAddress(new java.net.URL(endpoint)); 

call.setOperationName("Counter"); 

// 填写你要调用的方法名称 

String counter = (String) call.invoke(new Object[] { CounterName, password }); 

counterarr = counter.split(";"); 

res = (counterarr != null && counterarr.length == 4); 

} 

catch (Exception e) { 

} 

return res; 

} 


public String getDc() { 

return counterarr[3]; 

} 


public String getMc() { 

return counterarr[1]; 

} 


public String getTc() { 

return counterarr[0]; 

} 


public String getWc() { 

return counterarr[2]; 

} 

} 


到页面(test.jsp)上就简单了,我也把它贴在下面: 


<%@ page language="java" import="com.wallimn.WebService.CounterServiceClient" pageEncoding="GB18030"%> 

<% 

CounterServiceClient client = new CounterServiceClient(); 

client.getCounter("hello","123"); 

%> 


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 

<html> 

<head> 

<title>计数器测试页面</title> 


<meta http-equiv="pragma" content="no-cache"> 

<meta http-equiv="cache-control" content="no-cache"> 

<meta http-equiv="expires" content="0"> 

<meta http-equiv="keywords" content="wallimn,计数器,WebService"> 

<meta http-equiv="description" content="计数器使用示例"> 

</head> 


<body> 

<h2 align="center">计数器详情</h2> 

<hr/> 

总访问量:<%=client.getTc()%> <br/> 

今天访问量:<%=client.getDc()%> <br/> 

本周访问量:<%=client.getWc()%> <br/> 

本月访问量:<%=client.getMc()%> <br/> 

<hr/> 

</body> 

</html>