(一) 有关介绍 二进制数据在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 05881714148873.htm; 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 05881714148873.htm; } public void setFilename(String 05881714148873.htm) { this.05881714148873.htm = 05881714148873.htm; } public String getFilepath() { return filepath; } public void setFilepath(String filepath) { this.filepath = filepath; } public String toString() { return this.05881714148873.htm + 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#中调用这个服务.
|