(一) 有关介绍
二进制数据在WS中传送,可以有两种方式:
1 把数据直接作为xml文档中某元素的字节流,作为XML解析器要解析的一部分,很明显这种方式比较低效.
2 二进制数据作为附件,作为带外(out of band)数据随同XML发送, 提高了效率.目前这类处理有几个规范:
DIME(直接 Internet 消息封装),这个数据包装格式及其处理,只有微软在支持.
http://www.microsoft.com/china/msdn/archives/library/dnwebsrv/html/DIMEWSAttch.asp
http://www.zdnet.com.cn/developer/code/story/0,3800066897,39358789,00.htm
MTOM(SOAP 消息传输优化机制)和XOP(二进制 XML 优化封装);
在Sun规范JAX-RPC1.1中,要求使用SwA(SOAP with Attachments) 支持附件,为此Sun提供了SOAP with Attachments API for Java,带附件的SoapAPI(SAAJ),早期它和jaxm合在一起的,现在已经独立开来形成了soap包,这个API专门用来处理Soap附件的所有操作.
JAXRPC 1.1 规范定义了MIME类型到Java类型的影射.
MIME Type
Java Type
image/gif
java.awt.Image
image/jpeg
java.awt.Image
text/plain
java.lang.String
multipart/*
javax.mail.internet.MimeMultipart
text/xml or application/xml
javax.xml.transform.Source
JAXRPC 1.1 规范定义了这种情况:当绑定到上表中没有定义的类型影射或者是绑定到备用MIME类型时,它应该影射到 javax.activation.DataHandler.
wscompile工具中的选项-f:<features>用于类型影射的项:datahandleronly,该项指明总是把附件影射到DataHandler类型.
(二) 编写例子应用
文件清单
SEI接口类IImage.java,实现SEI接口的类IIMageImpl.java,描述文件信息的值类型类FileInfo.java,Web应用部署描述文件web.xml,WS发布配置文件jaxrpc-ri.xml,WS编译配置文件config-interface.xml,构建客户端桩的config-wsdl.xml,构建文件build.xml
客户端测试类:MainBrowser.java,ImageListProvider.java和ImageLabelProvider.java,测试使用了SWT和JFace,请适当配置你的Eclipse环境,引入适当的库.
这个例子是从SEI开始,绑定样式为 RPC.
IImage.java清单:
package com.bin;
import java.rmi.Remote;
import java.rmi.RemoteException;
import javax.activation.*;
import javax.xml.soap.*;
import java.util.*;
public interface IImage extends Remote {
public DataHandler fetchImg(String sn) throws RemoteException;
public ArrayList fetchImgs(String[] sn) throws RemoteException;
public SOAPMessage construcMsg(String[] fn) throws RemoteException;
public ArrayList fetchFileList() throws java.rmi.RemoteException;
public FileInfo getFileList(String fn) throws java.rmi.RemoteException;
}
实现类 ImageImpl.java清单:
package com.bin;
import javax.xml.soap.*;
import java.net.*;
import java.util.*;
import java.io.*;
import javax.activation.*;
import java.rmi.*;
import java.awt.*;
import javax.xml.rpc.ServiceException;
import javax.xml.rpc.server.*;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.rpc.handler.MessageContext;
import javax.servlet.ServletContext;
import com.sun.xml.rpc.server.*;
public class ImageImpl implements IImage, ServiceLifecycle {
ServletEndpointContext servletEndpointContext = null;
String binarypath = "";
ArrayList al;
ServletContext servletContext = null;
public void init(Object p0) throws ServiceException {
// Some logic to do upon service creation
servletEndpointContext = (ServletEndpointContext) p0;
servletContext = servletEndpointContext.getServletContext();
binarypath = servletContext.getInitParameter("BinaryPath");
}
public void destroy() {
// Some logic to do on service destruction - e.g. clean up JDBC
servletEndpointContext = null;
servletContext = null;
}
public ArrayList fetchFileList() throws RemoteException {
File file = new File(this.binarypath);
//System.out.println(this.binarypath);
if (al == null)
al = new ArrayList();
File[] c = file.listFiles();
for (int i = 0; i < c.length; i++) {
FileInfo fi = new FileInfo();
fi.setIsdir(c[i].isDirectory());
fi.setFilename(c[i].getName());
fi.setFilelength(c[i].length());
fi.setFilepath(c[i].getAbsolutePath());
fi.setCreatedate(new Date(c[i].lastModified()));
al.add(fi);
}
return al;
}
public DataHandler fetchImg(String sn) throws RemoteException {
File file = new File(sn);
DataHandler dataHandler = null;
try {
URL url = new URL(file.toURL().toString());
dataHandler = new DataHandler(url);
} catch (Exception ex) {
System.out.println(ex);
throw new RemoteException(ex.getMessage());
}
return dataHandler;
}
public ArrayList fetchImgs(String[] fn) throws RemoteException {
ArrayList al = new ArrayList();
try {
for (int i = 0; i < fn.length; i++) {
File file = new File(fn[i]);
// Create attachment part for image
URL url = new URL(file.toURL().toString());
DataHandler dataHandler = new DataHandler(url);
al.add(dataHandler);
}
} catch (Exception ex) {
System.out.println(ex);
throw new RemoteException(ex.getMessage());
}
return al;
}
public SOAPMessage construcMsg(String[] fn) throws RemoteException {
FileReader fr = null;
BufferedReader br = null;
String line = "";
SOAPMessage message = null;
try {
// Create message factory
MessageFactory messageFactory = MessageFactory.newInstance();
// Create a message
message = messageFactory.createMessage();
// Get the SOAP header and body from the message
// and remove the header
SOAPHeader header = message.getSOAPHeader();
SOAPBody body = message.getSOAPBody();
header.detachNode();
for (int i = 0; i < fn.length; i++) {
File file = new File(fn[i]);
// Create attachment part for image
URL url = new URL(file.toURL().toString());
DataHandler dataHandler = new DataHandler(url);
AttachmentPart attachment2 = message
.createAttachmentPart(dataHandler);
attachment2.setContentId(file.getName());
message.addAttachmentPart(attachment2);
}
} catch (IOException e) {
System.out.println("I/O exception: " + e.toString());
throw new RemoteException(e.getMessage());
} catch (Exception ex) {
ex.printStackTrace();
throw new RemoteException(ex.getMessage());
}
return message;
}
public FileInfo getFileList(String fn) throws RemoteException {
// TODO Auto-generated method stub
File file = new File(fn);
FileInfo fi = new FileInfo();
fi.setFilename(file.getName());
fi.setFilelength(file.length());
fi.setFilepath(file.getAbsolutePath());
fi.setCreatedate(new Date(file.lastModified()));
return fi;
}
}
FileInfo.java清单:
package com.bin;
import java.util.Date;
/**
* 这是一个值类型,既然是值类型,它满足:
1 必须有缺省构造器. 2 必须没有实现Remote(直接的或是间接的). 3属性类型必须是JAX-RPC支持的类型.
*/
public class FileInfo {
private String filename;
private String filepath;
private long filelength;
private Date createdate;
private boolean isdir = false;
public FileInfo() {
}
public Date getCreatedate() {
return createdate;
}
public void setCreatedate(Date createdate) {
this.createdate = createdate;
}
public long getFilelength() {
return filelength;
}
public void setFilelength(long filelength) {
this.filelength = filelength;
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public String getFilepath() {
return filepath;
}
public void setFilepath(String filepath) {
this.filepath = filepath;
}
public String toString() {
return this.filename + this.getFilepath() + this.getFilelength();
}
public boolean isIsdir() {
return isdir;
}
public void setIsdir(boolean isdir) {
this.isdir = isdir;
}
}
web.xml清单:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<context-param>
<param-name>BinaryPath</param-name>
<param-value>更改成你的本地文件路径</param-value>
</context-param>
<welcome-file-list>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.jws</welcome-file>
</welcome-file-list>
</web-app>
config-interface.xml清单:
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<service name="BinaryService" targetNamespace="urn:binary"
typeNamespace="urn:binary" packageName="com.binary">
<interface name="com.bin.IImage"
servantName="com.bin.ImageImpl" />
</service>
</configuration>
config-wsdl.xml清单:
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<wsdl location="http://localhost:8080/skysoft/binary?WSDL"
packageName="com.binary" />
</configuration>
jaxrpc-ri.xml清单:
<?xml version="1.0" encoding="UTF-8"?>
<webServices xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/dd"
version="1.0"
targetNamespaceBase="http://java.sun.com/xml/ns/jax-rpc/wsi/wsdl"
typeNamespaceBase="http://java.sun.com/xml/ns/jax-rpc/wsi/types"
urlPatternBase="/ws">
<endpoint name="binaryservice" displayName="Stock Example"
description="Binary Web Service endpoint"
interface="com.bin.IImage" implementation="com.bin.ImageImpl"
model="/WEB-INF/model.xml.gz" />
<endpointMapping endpointName="binaryservice" urlPattern="/binary" />
</webServices>
构建文件build.xml:依次运行build,create-war,deploy,genstaticstub等任务,这样本例中的WS所需要的文件全部生成.
<?xml version="1.0" encoding="GBK"?>
<project name="webservice" default="build" basedir=".">
<property name="jaxrpc.lib.dir2" value="D:\jwsdp-1.5\jaxrpc\lib">
</property>
<property name="jaxrpc.lib.dir" value="I:\jwsdp-1.6\jaxrpc\lib">
</property>
<property name="jaxrpc.lib.dir1" value="D:\Sun\AppServer\lib">
</property>
<property name="classes.dir" value=".\build\classes">
</property>
<property name="src.dir" value=".\build\src">
</property>
<property name="war.file" value="hello_raw.war">
</property>
<property name="mapping.file" value="mapping.xml">
</property>
<property name="tmp.dir" value=".\tmp">
</property>
<property name="nonclass.dir" value=".\build\nonclass">
</property>
<property name="build" value="${nonclass.dir}">
</property>
<property name="assemble" value=".\assemble">
</property>
<property name="assemble.war" value=".\assemble\war">
</property>
<property name="assemble.ear" value=".\assemble\ear">
</property>
<path id="jaxrpc-classpath">
<!--fileset dir="${jaxrpc.lib.dir}">
<include name="**/*.jar" />
</fileset-->
<fileset dir="D:\jdbc\postgresql">
<include name="*.jar" />
</fileset>
<fileset dir="I:\jwsdp-1.6\saaj\lib">
<include name="*.jar" />
</fileset>
<fileset dir="I:\jwsdp-1.6\jaxrpc\lib">
<include name="*.jar" />
</fileset>
<fileset dir="I:\jwsdp-1.6\jwsdp-shared\lib">
<include name="**/*.jar" />
</fileset>
</path>
<path id="compile.classpath">
<!--fileset dir="${jaxrpc.lib.dir}">
<include name="**/*.jar" />
</fileset-->
<fileset dir="I:\jwsdp-1.6\jwsdp-shared\lib">
<include name="**/*.jar" />
</fileset>
<fileset dir="i:\jwsdp-1.6\jaxrpc\lib">
<include name="**/*.jar" />
</fileset>
<fileset dir="I:\jwsdp-1.6\saaj\lib">
<include name="*.jar" />
</fileset>
</path>
<taskdef name="wscompile" classpathref="jaxrpc-classpath" classname="com.sun.xml.rpc.tools.ant.Wscompile">
</taskdef>
<taskdef name="wsdeploy" classpathref="jaxrpc-classpath" classname="com.sun.xml.rpc.tools.ant.Wsdeploy">
</taskdef>
<target name="prepare">
<mkdir dir="${src.dir}" />
<mkdir dir="${nonclass.dir}" />
<mkdir dir="${classes.dir}" />
<mkdir dir="${assemble}" />
<mkdir dir="${assemble.war}" />
<mkdir dir="${assemble.ear}" />
<mkdir dir="${tmp.dir}" />
</target>
<target name="help" description="generates ws classes">
<echo message="help ........" />
<wscompile help="true">
<classpath refid="compile.classpath" />
</wscompile>
</target>
<target name="build" depends="prepare" description="generates ws classes">
<echo message="build the WAR...." />
<wscompile import="false" define="true" gen="false" features="wsi,documentliteral" keep="true" base="${classes.dir}" sourceBase="${src.dir}" classpath=".\classes" nonClassDir="${nonclass.dir}" model="model.xml.gz" xPrintStackTrace="true" config="config-interface.xml" verbose="true">
<classpath refid="compile.classpath" />
</wscompile>
</target>
<target name="deploy" description="deploy ws">
<echo message="deploy the WAR...." />
<wsdeploy keep="false" verbose="true" tmpDir="${tmp.dir}" outWarFile="skysoft.war" inWarFile="hello_raw.war">
<classpath refid="compile.classpath" />
</wsdeploy>
</target>
<target name="create-war" description="Packages the WAR file">
<echo message="Creating the WAR...." />
<delete file="${assemble.war}/${war.file}" />
<delete dir="${assemble.war}/WEB-INF" />
<copy todir="${assemble.war}/WEB-INF/classes/">
<fileset dir="./classes" includes="**/*.class" excludes="**/*Client.class, **/*.wsdl, **/*mapping.xml" />
</copy>
<copy todir="${assemble.war}/WEB-INF/lib/">
<fileset dir="./lib" includes="**/*.jar" excludes="**/*Client.class, **/*.wsdl, **/*mapping.xml" />
</copy>
<copy file="jaxrpc-ri.xml" todir="${assemble.war}/WEB-INF" />
<copy file="model.xml.gz" todir="${assemble.war}/WEB-INF" />
<war destfile="${assemble.war}/${war.file}" webxml="./web.xml" filesonly="true">
<fileset dir="${assemble.war}" includes="WEB-INF/**, build/**" />
</war>
<copy file="${assemble.war}/${war.file}" todir="." />
</target>
<target name="genstaticstub">
<echo message="gen statics tub" />
<wscompile client="true" keep="true" base="./staticstub" sourceBase="./staticstub" xPrintStackTrace="true" config="config-wsdl.xml" verbose="true">
<classpath refid="compile.classpath" />
</wscompile>
</target>
</project>
接下来,发布skysoft.war到你的web服务环境,本例使用的是Tomcat5,检查http://localhost:8080/skysoft/binary?WSDL是否装入.
发布成功后,用Eclipse新建一个java项目,把静态桩文件悉数拷贝到该项目的src下,然后编写以下类:
MainBrowser窗口类,显示图文件.
package swtui;
import java.io.IOException;
import java.rmi.RemoteException;
import java.util.ArrayList;
import javax.xml.rpc.Stub;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.widgets.*;
import com.binary.BinaryService_Impl;
import com.binary.FileInfo;
import com.binary.IImage;
import com.binary.IImage_Stub;
public class MainBrowser extends ApplicationWindow {
Label label;
ImageLabelProvider canvas;
Image image;
public MainBrowser(Shell arg0) {
super(arg0);
// TODO Auto-generated constructor stub
}
public MainBrowser() {
super(null);
addStatusLine();
// TODO Auto-generated constructor stub
}
/**
* ImageLoader Currently supported image formats are:
*
* BMP (Windows Bitmap) ICO (Windows Icon) JPEG GIF PNG
*/
protected Control createContents(Composite parent) {
getShell().setText("JFace File Explorer");
SashForm sash_form = new SashForm(parent, SWT.HORIZONTAL | SWT.NULL);
TableViewer tbv = new TableViewer(sash_form, SWT.BORDER
| SWT.FULL_SELECTION | SWT.MULTI);
ImageListProvider img = new ImageListProvider();
tbv.setContentProvider(img);
tbv.setInput(getFileInfo());
tbv.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
IStructuredSelection selection = (IStructuredSelection) event
.getSelection();
FileInfo fi = (FileInfo) selection.getFirstElement();
if (!fi.isIsdir()) {
showImage(fi.toString());
canvas.adjustSize();
// canvas.redraw();
}
setStatus(fi.toString());
}
});
canvas = new ImageLabelProvider(sash_form, SWT.SHELL_TRIM
| SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE | SWT.V_SCROLL
| SWT.H_SCROLL | SWT.CENTER);
return sash_form;
}
public static void main(String[] args) {
MainBrowser w = new MainBrowser();
w.setBlockOnOpen(true);
w.open();
}
public void showImage(String fn) {
IImage_Stub stub = (IImage_Stub) createProxy();
IImage hello = (IImage) stub;
ImageData idata = null;
try {
idata = new ImageData(hello.fetchImg(fn).getInputStream());
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ImageDescriptor id = ImageDescriptor.createFromImageData(idata);
if (image != null)
image.dispose();
image = id.createImage();
// label.setImage(id.createImage());
// canvas.setData(image);
canvas.setImage(image);
}
public Object getFileInfo() {
IImage_Stub stub = (IImage_Stub) createProxy();
IImage hello = (IImage) stub;
ArrayList al = null;
try {
al = hello.fetchFileList();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return al;
}
private Stub createProxy() {
return (Stub) (new BinaryService_Impl().getIImagePort());
}
}
ImageLabelProvider标签类,能滚动图象的组件.
package swtui;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.*;
public class ImageLabelProvider extends Canvas {
ScrollBar hBar = null;
ScrollBar vBar = null;
private Image image;
public Image getImage() {
return image;
}
public void setImage(Image image) {
this.image = image;
setData(image);
}
public ImageLabelProvider() {
this(null, SWT.CENTER);
// TODO Auto-generated constructor stub
}
public ImageLabelProvider(Composite arg0, int arg1) {
super(arg0, arg1);
hBar = getHorizontalBar();
vBar = getVerticalBar();
setListener();
// TODO Auto-generated constructor stub
}
public void adjustSize() {
if(image==null)
return;
final Point origin = new Point(0, 0);
Rectangle rect = image.getBounds();
Rectangle client = getClientArea();
hBar.setMaximum(rect.width);
vBar.setMaximum(rect.height);
hBar.setThumb(Math.min(rect.width, client.width));
vBar.setThumb(Math.min(rect.height, client.height));
int hPage = rect.width - client.width;
int vPage = rect.height - client.height;
int hSelection = hBar.getSelection();
int vSelection = vBar.getSelection();
if (hSelection >= hPage) {
if (hPage <= 0)
hSelection = 0;
origin.x = -hSelection;
}
if (vSelection >= vPage) {
if (vPage <= 0)
vSelection = 0;
origin.y = -vSelection;
}
redraw();
}
private void setListener() {
final Point origin = new Point(0, 0);
hBar.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
int hSelection = hBar.getSelection();
int destX = -hSelection - origin.x;
Rectangle rect = image.getBounds();
scroll(destX, 0, 0, 0, rect.width, rect.height, false);
origin.x = -hSelection;
}
});
vBar.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
int vSelection = vBar.getSelection();
int destY = -vSelection - origin.y;
Rectangle rect = getBounds();
scroll(0, destY, 0, 0, rect.width, rect.height, false);
origin.y = -vSelection;
}
});
addListener(SWT.Resize, new Listener() {
public void handleEvent(Event e) {
Rectangle rect = image.getBounds();
Rectangle client = getClientArea();
hBar.setMaximum(rect.width);
vBar.setMaximum(rect.height);
hBar.setThumb(Math.min(rect.width, client.width));
vBar.setThumb(Math.min(rect.height, client.height));
int hPage = rect.width - client.width;
int vPage = rect.height - client.height;
int hSelection = hBar.getSelection();
int vSelection = vBar.getSelection();
if (hSelection >= hPage) {
if (hPage <= 0)
hSelection = 0;
origin.x = -hSelection;
}
if (vSelection >= vPage) {
if (vPage <= 0)
vSelection = 0;
origin.y = -vSelection;
}
redraw();
}
});
addListener(SWT.Paint, new Listener() {
public void handleEvent(Event e) {
GC gc = e.gc;
gc.drawImage(image, origin.x, origin.y);
Rectangle rect = image.getBounds();
Rectangle client = getClientArea();
int marginWidth = client.width - rect.width;
if (marginWidth > 0) {
gc.fillRectangle(rect.width, 0, marginWidth, client.height);
}
int marginHeight = client.height - rect.height;
if (marginHeight > 0) {
gc.fillRectangle(0, rect.height, client.width,
marginHeight);
}
}
});
}
}
ImageListProvider.java代码清单:
package swtui;
import java.io.File;
import java.rmi.RemoteException;
import java.util.ArrayList;
import javax.xml.rpc.Stub;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.Viewer;
import com.binary.*;
public class ImageListProvider implements IStructuredContentProvider{
public Object[] getElements1(Object element) {
Object[] kids = ((ArrayList) element).toArray();
return kids == null ? new Object[0] : kids;
}
private Stub createProxy() {
return (Stub) (new BinaryService_Impl().getIImagePort());
}
public void dispose() {
}
public void inputChanged(Viewer arg0, Object arg1, Object arg2) {
}
}
到此,例子结束,应该说使用Eclipse,借助于插件,开发一个web服务还是相当快捷的. 本例可通过扩展客户端以处理其它格式的文件.
下一篇将介绍如何在C#中调用这个服务.