[j2me]利用kSOAP让MIDP设备与WebService之间传递类对象

<iframe align="center" marginwidth="0" marginheight="0" src="http://www.zealware.com/csdnblog336280.html" frameborder="0" width="336" scrolling="no" height="280"></iframe>

[j2me]利用kSOAPMIDP设备与

WebService之间传递类对象

编写者

日期

关键词

郑昀@ultrapower

2005-8-14

J2me webservice soa ksoap serialization MIDP CLDC

无线设备联网的概念

中国移动GPRS网络的连接方式有两种类型,一种是WAPGPRS,接入名称叫CMWAP,一种是KJavaGPRS,接入名称叫CMNET。也就是说,我们使用J2ME访问远端服务器的Web Service,将走移动的CMNET接入点。

MIDP的概念

MIDP的概念我就不再说了,有一个不错的图片,可以作为注脚:

<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_s1026" style="WIDTH: 456.9pt; HEIGHT: 307.75pt; mso-position-horizontal-relative: char; mso-position-vertical-relative: line" fillcolor="#bbe0e3" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/VICTOR~1/LOCALS~1/Temp/msohtml1/01/clip_image001.png"></imagedata><wrap type="none"></wrap><anchorlock></anchorlock></shape>

kSOAPJSR172

原则上,我们可以通HttpConnection接口访问远端Web服务器

但是,对于远端的Web Service,我们还有两种解决方案:

l kSOAP

l JSR172

下面,我们来介绍一下:

kSOAP

http://ksoap.objectweb.org/上,你可以下载一系列的包:

ksoap-j2se.zip

ksoap-midp.zip

ksoap-servlet.zip

这是一个开源项目,它帮助你完成SOAP的解析和调用工作。虽然kSOAP小组现在已经停止了开发,但是他们留下来的是一个能够工作的kSOAP

让我们先熟悉一下即将用到的kSOAP的接口:

ListAdapter.cs

org.ksoap. ClassMap

org.ksoap. SoapObject

org.ksoap.transport. HttpTransport

ClassMap能够让你指定序列化/反序列化SOAP Messages的方式;

SoapObject让你自如地构造SOAP调用;

HttpTransport为你屏蔽了Internet访问/请求和获取服务器SOAP的细节。

JSR172

http://jcp.org/en/jsr/detail?id=172,您可以找到“J2METM Web Services Specification”的04年最终发布的规范

http://jcp.org/aboutJava/communityprocess/final/jsr172/index.html

还有http://java.sun.com/products/wsa/,这里的WSA指的是J2ME Web Services APIs

如何正地动设备融入到Web Services中去呢?就需要使得PDA、手机等成Web Services的客端,那么这设备至少应该具有XML信息的能力。J2ME Web Services范(JSR172)正是了解决这个问题

首先,该规范的制J2ME平台增加大功能:一是使其能够远访问基于SOAP/XMLWeb Services;二是使其具有解析XML据的能力。实现这两大功能,JSR172新定了提供相功能的两个包。这两个包占用存非常少,XML-RPC部分大需要25-30KB的空,而XML解析器需要35KB左右。

但是目前上市的手机很少有支持JSR172的,所以我们不得不使用kSOAP

第一小步,Web Service传递String

Web Service传递StringMIDP是一件很简单的事情

在服务器端,你的由AXIS支撑的Web Service可以这么输出:

服务器端

public class SimpleKSoapWS {

public SimpleKSoapWS () {

}

public String foo(String username, String password) {

return “fooResult”;

}

}

我们来说说,kSOAP是如何调用Web Service的:

SoapObject,一个高度抽象化的类,让无线设备完成SOAP调用。可以调用它的addProperty方法填写要调用的Web Service方法的参数。如下面代码所示:

SoapObject soap = new SoapObject(serviceNamespace, methodName);

SoapObject构造函数的两个参数的意思分别是:

serviceNamespace – 你的Web Service的命名空间;

methodName – 你要调用方法的名字。

然后,按照Web Service方法参数的顺序,依次调用

soap.addProperty( "username", "user" );

soap.addProperty( "password", "pass" );

一般来说,对于仅仅是String的返回值,还用不着ClassMap

接下来就要声明

HttpTransport tx = new HttpTransport(serviceUrl, methodName);

了。这是一个强大的helper类,来完成Http-call transport process,它封装了network的一切,你完全不用考虑序列化消息。方法HttpTransport.call()自己就能够发送请求给服务器、接收服务器响应并序列化SOAP消息,如下所示:

Object Response = tx.call(request);

客户端的MIDlet的按键事件函数这么写,即可:

midlet

/*

* Respond to commands, including exit

* On the exit command, cleanup and notify that the MIDlet has been destroyed.

*/

public void commandAction(Command c, Displayable s) {

if (c == exitCommand)

{

destroyApp(false);

notifyDestroyed();

}

if (c == connectCommand)

{

//匿名内部Thread,访问远程服务。

Thread fetchThread=new Thread()

{

public void run(){

try

{

HttpTransport transport = null;

String serviceNamespace =

"http://localhost:8080/ SimpleWS /services/ SimpleKSoapWS ";

String methodName = "foo";

String serviceUrl =

"http://localhost:8080/ SimpleWS /services/ SimpleKSoapWS?wsdl";

ClassMap classMap = new ClassMap(true);

classMap.prefixMap = new PrefixMap( classMap.prefixMap, "ns",

serviceNamespace );

transport = new HttpTransport(serviceUrl, soapAction);

transport.setClassMap(classMap);

SoapObject soap = new SoapObject(

serviceNamespace, methodName);

soap.addProperty( "username", "user" );

soap.addProperty( "password", "pass" );

SoapObject response = (SoapObject)transport.call(soap);

String sResponse = (String)response;

}

catch (Exception e) {

e.printStackTrace ();

}

}

};

fetchThread.start();

display.setCurrent(mainForm);

}

这里的知识点是,我们需要

Thread fetchThread=new Thread(){…};

来另起一个线程来完成对Web Service的调用。否则,你的模拟器是会一动不动的。

第二小步,Web Service传递较为复杂的类

下面我们讲述如何MIDP设备和Web Service之间传递较为复杂的类,比如这个类中不但有String类型成员变量,还有Vector之类的复杂类型

kSoapFAQ上看,他们推荐使用KvmSerializable以及 ClassMap传递自定义类,但是我一直没有试验成功。

我还是按照能试验出来的办法讲述一下步骤吧:

大致思路就是,在服务器端将类实例按照一定规格(一个一个的成员变量写)序列化为byte[],将这个byte[]数组返回给kSOAPkSOAP收到之后反序列化,将byte[]一段一段地读入类实例。

Web Service服务器端的做法

先来定义要传递的wsTeam类:

类定义

public class wsTeam{

private String wsReturnCode;

private String wsPersonCount;

public StringVector wsvPersonName;

public byte[] serialize();

public static wsTeam deserialize(byte[] data) ;

}

其中,StringVector类是另外一个自定义类,就是简单地把String[]封装了一下,便于操作。StringVector类定义在附录中可以找到。

服务器端主要是序列化,所以我们来讲讲wsTeambyte[] serialize()函数。

序列化

public byte[] serialize() {

ByteArrayOutputStream baos = new ByteArrayOutputStream();

DataOutputStream dos = new DataOutputStream(baos);

try

{

dos.writeUTF(wsReturnCode);

dos.writeUTF(wsPersonCount);

wsvPersonName.writeObject(dos);

baos.close();

dos.close();

}

catch(Exception exc)

{

exc.printStackTrace();

}

return baos.toByteArray();

}

这样,类实例就可以把自己序列化为byte[]数组。

那么,Web Service可以这么提供:

服务器端

public class SimpleKSoapWS {

public SimpleKSoapWS () {

}

public byte[] foo2(String username, String password) {

wsTeam obj= new wsTeam ();

return obj.serialize();

}

}

到了MIDP设备上,要能够从byte[]恢复出wsTeam类实例才行。

StringVector的序列化方法writeObject也很简单,先写入字符串数组的大小,然后再将每一个元素写入,如下所示:

StringVector的序列化

public class StringVector

{…

public synchronized void writeObject(java.io.DataOutputStream s)

throws java.io.IOException

{

// Write out array length

s.writeInt(count);

// Write out all elements in the proper order.

for (int i=0; i<count i></count>

{

s.writeUTF(data[i]);

}

}

}

MIDP设备的做法

和前面的MIDlet代码差不多,只不过要我们的ClassMap出场了

使用ClassMap

ClassMap classMap = new ClassMap();

(new MarshalBase64()).register(classMap);

HttpTransport tx = new HttpTransport(serviceUrl, methodName);

tx.setClassMap( classMap );

tx.debug = true;

MarshalBase64类定义在附录中可以找到。

这样,后面才能将接收到的SoapObject强制转换为byte[]

转换

Object Response = tx.call(request);

System.out.println( tx.responseDump );

byte[] by = (byte[])Response;

然后,再调用

反序列化

wsTeam wc = wsTeam.deserialize(by);

这样,在无线设备上就得到了wsTeam类实例了。

wsTeamdeserialize是这么定义的:

反序列化

public class StringVector

{…

public static wsTeam deserialize(byte[] data) {

ByteArrayInputStream bais = new ByteArrayInputStream(data);

DataInputStream dis = new DataInputStream(bais);

wsTeam wc = new wsTeam();

try

{

wc.wsReturnCode = dis.readUTF();

wc.wsPersonCount = dis.readUTF();

wc. wsvPersonName.readObject(dis);

bais.close();

dis.close();

}

catch(Exception exc)

exc.printStackTrace();

}

return wc;

}

…}

StringVector的反序列化方法readObject也很简单,先读入字符串数组的大小,就自行新建一个同样大小的字符串数组,然后再将每一个元素写入这个数组,如下所示:

StringVector的反序列化

public class StringVector

{…

public synchronized void readObject(java.io.DataInputStream s)

throws java.io.IOException, ClassNotFoundException

{

// Read in array length and allocate array

int arrayLength = s.readInt();

data = new String[arrayLength];

// 同步data的大小

count = arrayLength;

// Read in all elements in the proper order.

for (int i=0; i<arraylength i></arraylength>

{

data[i] = s.readUTF();

}

}…

}

通过上面的反序列化,我们就可以通过

for (int i=0; i<wc.><span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: gray; FONT-FAMILY: Arial; mso-bidi-font-size: 10.5pt; mso-font-kerning: 0pt"> wsvPersonName</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-bidi-font-size: 10.5pt; mso-font-kerning: 0pt">.size(); i++) <p></p></span></wc.>

{

System.out.println("" + i +"个人:" +

wc. wsvPersonName.getStringAt(i));

}

来打印MIDlet上收到的类对象中的StringVector成员变量了。

小结

利用相同的办法,您还可以在无线设备和Web Service之间,传递各种各样的类对象,类里面也包含了各种类型的成员变量。

编写者

日期

关键词

郑昀@ultrapower

2005-8-19

J2me webservice soa ksoap serialization MIDP CLDC

附录AStringVector

StringVector

/**

* Vector主要用来保存各种类型的对象(包括相同类型和不同类型的对象)。

* 但是在一些情况下使用会给程序带来性能上的影响。这主要是由Vector类的两个特点所决定的。

* 第一,Vector提供了线程的安全保护功能。即使Vector类中的许多方法同步。

* 但是如果你已经确认你的应用程序是单线程,这些方法的同步就完全不必要了。

* 第二,在Vector查找存储的各种对象时,常常要花很多的时间进行类型的匹配。

* 而当这些对象都是同一类型时,这些匹配就完全不必要了。

* 因此,有必要设计一个单线程的,保存特定类型对象的类或集合来替代Vector

*/

package com.ultrapower.helper;

/**

* @author VictorZheng

*

*/

public class StringVector

{

// 这儿的transient标示这个属性不需要自动序列化

private transient String[] data;

private int count;

public int size()

{

return count;

}

public StringVector()

{

// default size is 10

this(10);

}

public StringVector(int initialSize)

{

data = new String[initialSize];

}

public void add(String str)

{

// ignore null strings

if(str == null) { return; }

ensureCapacity(count + 1);

data[count++] = str;

}

private void ensureCapacity(int minCapacity)

{

int oldCapacity = data.length;

if (minCapacity > oldCapacity)

{

String oldData[] = data;

int newCapacity = oldCapacity * 2;

data = new String[newCapacity];

System.arraycopy(oldData, 0, data, 0, count);

}

}

public void remove(String str)

{

if(str == null)

{

return; // ignore null str

}

for(int i = 0; i

{

// check for a match

if(data[i].equals(str))

{

System.arraycopy(data,i+1,data,i,count-1); // copy data

// allow previously valid array element be gc'd

data[--count] = null;

return;

}

}

}

public final String getStringAt(int index)

{

if(index

{ return null; }

else if(index > count)

{

return null; // index is > # strings

}

else

{

return data[index]; // index is good

}

}

public synchronized void writeObject(java.io.DataOutputStream s)

throws java.io.IOException

{

// Write out array length

s.writeInt(count);

// Write out all elements in the proper order.

for (int i=0; i<count i></count>

{

s.writeUTF(data[i]);

}

}

public synchronized void readObject(java.io.DataInputStream s)

throws java.io.IOException, ClassNotFoundException

{

// Read in array length and allocate array

int arrayLength = s.readInt();

data = new String[arrayLength];

// 同步data的大小

count = arrayLength;

// Read in all elements in the proper order.

for (int i=0; i<arraylength i></arraylength>

{

data[i] = s.readUTF();

}

}

}

附录BMarshalBase64

MarshalBase64

/**

* @author VictorZheng

*

*/

import java.io.IOException;

import org.kobjects.serialization.ElementType;

import org.ksoap.ClassMap;

import org.ksoap.Marshal;

import org.ksoap.Soap;

import org.ksoap.SoapParser;

import org.ksoap.SoapWriter;

/** Base64 (de)serializer */

public class MarshalBase64 implements Marshal {

static byte [] BA_WORKAROUND = new byte [0];

public static Class BYTE_ARRAY_CLASS = BA_WORKAROUND.getClass ();

public Object readInstance (SoapParser parser,

String namespace, String name,

ElementType expected) throws IOException {

parser.parser.read (); // start tag

Object result = Base64.decode

(parser.parser.readText ());

parser.parser.read (); // end tag

return result;

}

public void writeInstance (SoapWriter writer,

Object obj) throws IOException {

writer.writer.write (Base64.encode ((byte[]) obj));

}

public void register (ClassMap cm) {

cm.addMapping

(cm.xsd, "base64Binary",

MarshalBase64.BYTE_ARRAY_CLASS, this);

cm.addMapping

(Soap.ENC, "base64",

MarshalBase64.BYTE_ARRAY_CLASS, this);

}

}




你可能感兴趣的:(webservice)