1、CXF简单的HelloWorld、复杂对象传递、图片上传(MTOM)
(软件版本CXF2.3.1和JDK1.6.0_10)
可以用wsimport –keep http://localhost:9000/cxf?wsdl 生成客户端
也可以用CXF的wsdl2java命令生成客户端代码
使用Cxf必须创建一个服务接口(客户端使用)、这个接口的实现类(服务端处理客户端请求的相关业务)、服务端启动类(发布服务)和客户端启动类(访问服务)。
注:Exception in thread “main” java.lang.NoClassDefFoundError:
Com/sun/mail/util/LineInputStream
原因:JavaEE版本和JavaMail的版本不一致。
解决方法:windowàpreferencesàjava,把版本改成一致就可以了。
1.1helloword
首先导入cxf的所有包。
以HelloWorld为例,服务接口:
package com.test;
import javax.jws.WebService;
@WebService//必须注解WebService,让cxf知道这个接口是发布的WebService
publicinterface HelloWorld
{
public String sayHi(Stringname);
}
服务实现类:
package com.test;
import javax.jws.WebService;
@WebService
publicclass HelloWorldImplimplements HelloWorld
{
publicString sayHi(String name)
{
System.out.println("Hello " + name);
return"Hello " + name;
}
}
服务端启动类:
package com.server;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
import com.test.HelloWorldImpl;
publicclass ServerStart
{
protected ServerStart()throws Exception
{
HelloWorldImpl helloWorld =new HelloWorldImpl();
String address ="http://localhost:9000/cxf";
/**
*也可以使用注释的方式发布
*/
//Endpoint.publish(address, helloWorld);
//发布服务
JaxWsServerFactoryBean server =new JaxWsServerFactoryBean();
server.setServiceClass(helloWorld.getClass());
server.setAddress(address);
server.create();
}
publicstaticvoid main(String[] args)throws Exception
{
new ServerStart();
}
}
客户端启动类:
package com.client;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import com.test.HelloWorld;
publicclass ClientStart
{
publicstaticvoid main(String args[])throws Exception
{
JaxWsProxyFactoryBean factory =newJaxWsProxyFactoryBean();
factory.setAddress("http://localhost:9000/cxf");
HelloWorld client = factory.create(HelloWorld.class);
//调用客户端方法并打印在控制台
System.out.println(client.sayHi("cxf"));
}
}
1.2、CXF复杂对象的传递
第一步:创建存储复杂对象的类(因为WebServices的复杂对象的传递,一定要借助第三方对象(即自定义对象)来实现)
- package com.ws.model;
- import java.util.ArrayList;
- import java.util.HashMap;
- public class Users {
- private ArrayList<UserInfo> userList;// 不要用List类型
- private HashMap<Integer, UserInfo> userMap;// 不要用Map类型
- private UserInfo[] userArray;
- // geter/seter 方法
- }
package com.ws.model;
import java.util.ArrayList;
import java.util.HashMap;
public class Users {
private ArrayList<UserInfo> userList; // 不要用List类型
private HashMap<Integer, UserInfo> userMap; // 不要用Map类型
private UserInfo[] userArray;
// geter/seter 方法
}
第二步:创建WebServices的服务端接口和实现类
- package com.ws.services;
- import javax.jws.WebService;
- import com.ws.model.UserInfo;
- import com.ws.model.Users;
- @WebService
- public interface IUserServices {
- public Users getAllUsers();
- public Users getUsersMap();
- public Users getUsersArray();
- }
package com.ws.services;
import javax.jws.WebService;
import com.ws.model.UserInfo;
import com.ws.model.Users;
@WebService
public interface IUserServices {
public Users getAllUsers();
public Users getUsersMap();
public Users getUsersArray();
}
- package com.ws.services.impl;
- import java.util.ArrayList;
- import java.util.HashMap;
- import javax.jws.WebService;
- import com.ws.model.UserInfo;
- import com.ws.model.Users;
- import com.ws.services.IUserServices;
- @WebService
- publicclass UserServicesImpl implements IUserServices {
- public Users getAllUsers() {
- ArrayList<UserInfo> list = new ArrayList<UserInfo>();
- list.add(new UserInfo("vicky",23));
- list.add(new UserInfo("ivy",26));
- list.add(new UserInfo("simon",26));
- list.add(new UserInfo("carol",29));
- Users users = new Users();
- users.setUserList(list);
- return users;
- }
- public Users getUsersMap() {
- HashMap<Integer, UserInfo> map =new HashMap<Integer, UserInfo>();
- map.put(23,new UserInfo("vicky",23));
- map.put(22,new UserInfo("caty",22));
- map.put(24,new UserInfo("leah",24));
- map.put(25,new UserInfo("kelly",25));
- map.put(27,new UserInfo("ivy",27));
- map.put(26,new UserInfo("simon",26));
- map.put(29,new UserInfo("carol",29));
- Users users = new Users();
- users.setUserMap(map);
- return users;
- }
- public Users getUsersArray() {
- UserInfo[] userInfo = new UserInfo[5];
- for(int i=0;i<5;i++){
- UserInfo info = new UserInfo();
- info.setUserAge(23+i);
- info.setUserName("Array"+(i+1));
- userInfo[i] = info;
- }
- Users users = new Users();
- users.setUserArray(userInfo);
- return users;
- }
- }
package com.ws.services.impl;
import java.util.ArrayList;
import java.util.HashMap;
import javax.jws.WebService;
import com.ws.model.UserInfo;
import com.ws.model.Users;
import com.ws.services.IUserServices;
@WebService
public class UserServicesImpl implements IUserServices {
public Users getAllUsers() {
ArrayList<UserInfo> list = new ArrayList<UserInfo>();
list.add(new UserInfo("vicky",23));
list.add(new UserInfo("ivy",26));
list.add(new UserInfo("simon",26));
list.add(new UserInfo("carol",29));
Users users = new Users();
users.setUserList(list);
return users;
}
public Users getUsersMap() {
HashMap<Integer, UserInfo> map = new HashMap<Integer, UserInfo>();
map.put(23, new UserInfo("vicky",23));
map.put(22, new UserInfo("caty",22));
map.put(24, new UserInfo("leah",24));
map.put(25, new UserInfo("kelly",25));
map.put(27, new UserInfo("ivy",27));
map.put(26, new UserInfo("simon",26));
map.put(29, new UserInfo("carol",29));
Users users = new Users();
users.setUserMap(map);
return users;
}
public Users getUsersArray() {
UserInfo[] userInfo = new UserInfo[5];
for(int i=0;i<5;i++){
UserInfo info = new UserInfo();
info.setUserAge(23+i);
info.setUserName("Array"+(i+1));
userInfo[i] = info;
}
Users users = new Users();
users.setUserArray(userInfo);
return users;
}
}
第三步:创建WebServices的服务端
- package com.test;
- import javax.xml.ws.Endpoint;
- import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
- import com.ws.services.impl.UserServicesImpl;
- publicclass ServerTest {
- public ServerTest(){
- // 发布User服务接口
- Endpoint.publish("http://localhost:8090/userInfoServices",new UserServicesImpl());
- }
- public static void main(String[] args) {
- // 启动服务
- new ServerTest();
- System.out.println("Server ready...");
- try {
- Thread.sleep(1000*300);//休眠五分分钟,便于测试
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("Server exit...");
- System.exit(0);
- }
- }
package com.test;
import javax.xml.ws.Endpoint;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
import com.ws.services.impl.UserServicesImpl;
public class ServerTest {
public ServerTest(){
// 发布User服务接口
Endpoint.publish("http://localhost:8090/userInfoServices", new UserServicesImpl());
}
public static void main(String[] args) {
// 启动服务
new ServerTest();
System.out.println("Server ready...");
try {
Thread.sleep(1000*300);//休眠五分分钟,便于测试
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Server exit...");
System.exit(0);
}
}
第四步:创建WebServices的客户端,并测试
1、将服务端创建的复杂对象的类和接口copy到客户工程中,且路径要与服务端一致;
2、新建测试类进行测试
- package com.ws.client;
- import java.util.List;
- import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
- import com.ws.model.UserInfo;
- import com.ws.model.Users;
- import com.ws.server.IUserServices;
- publicclass UserTest {
- public static void main(String[] args) {
- //创建WebService客户端代理工厂
- JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
- //注册WebService接口
- factory.setServiceClass(IUserServices.class);
- //设置WebService地址
- factory.setAddress("http://localhost:8090/userInfoServices");
- IUserServices userServices = (IUserServices)factory.create();
- System.out.println("invoke userinfo webservice...");
- // 测试Map
- // testMap(userServices);
- // 测试List
- // testList(userServices);
- // 测试Array
- // testArray(userServices);
- System.exit(0);
- }
- public static void testArray(IUserServices userServices){
- Users users = userServices.getUsersArray();
- if(users!=null){
- UserInfo[] array = users.getUserArray();
- for(UserInfo info:array){
- System.out.println("UserName: "+info.getUserName());
- System.out.println("UserAge : "+info.getUserAge());
- }
- }
- }
- publicstatic void testList(IUserServices userServices){
- Users users = userServices.getAllUsers();
- if(users!=null){
- List<UserInfo> list = users.getUserList();
- for(UserInfo info:list){
- System.out.println("UserName: "+info.getUserName());
- System.out.println("UserAge : "+info.getUserAge());
- }
- }
- }
- public static void testMap(IUserServices userServices){
- Users users = userServices.getUsersMap();
- if(users!=null){
- UserInfo info = users.getUserMap().get(23);
- System.out.println("UserName: "+info.getUserName());
- System.out.println("UserAge : "+info.getUserAge());
- }
- }
- }
package com.ws.client;
import java.util.List;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import com.ws.model.UserInfo;
import com.ws.model.Users;
import com.ws.server.IUserServices;
public class UserTest {
public static void main(String[] args) {
//创建WebService客户端代理工厂
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
//注册WebService接口
factory.setServiceClass(IUserServices.class);
//设置WebService地址
factory.setAddress("http://localhost:8090/userInfoServices");
IUserServices userServices = (IUserServices)factory.create();
System.out.println("invoke userinfo webservice...");
// 测试Map
// testMap(userServices);
// 测试List
// testList(userServices);
// 测试Array
// testArray(userServices);
System.exit(0);
}
public static void testArray(IUserServices userServices){
Users users = userServices.getUsersArray();
if(users!=null){
UserInfo[] array = users.getUserArray();
for(UserInfo info:array){
System.out.println("UserName: "+info.getUserName());
System.out.println("UserAge : "+info.getUserAge());
}
}
}
public static void testList(IUserServices userServices){
Users users = userServices.getAllUsers();
if(users!=null){
List<UserInfo> list = users.getUserList();
for(UserInfo info:list){
System.out.println("UserName: "+info.getUserName());
System.out.println("UserAge : "+info.getUserAge());
}
}
}
public static void testMap(IUserServices userServices){
Users users = userServices.getUsersMap();
if(users!=null){
UserInfo info = users.getUserMap().get(23);
System.out.println("UserName: "+info.getUserName());
System.out.println("UserAge : "+info.getUserAge());
}
}
}
第五步:运行服务端,验证webservices服务是否发布成功
第六步:运行客户端,验证是否成功调用webservices服务
注:在做webServices复杂对象传递时,返回值的类型不要用接口类型。例如(List 应该换成ArrayList ,Map应该换成HashMap)
1.3、CXF图片上传(MTOM)
附件上传需要借助MTOM,也需要一个实体类,用来处理上传的附件。
实体类:
publicclass Resume {
private StringcandidateName;//附件名
private StringresumeFileType;//附件类型
private DataHandlerresume;//附件
public String getCandidateName() {
returncandidateName;
}
publicvoid setCandidateName(String candidateName) {
this.candidateName = candidateName;
}
public String getResumeFileType() {
returnresumeFileType;
}
publicvoid setResumeFileType(String resumeFileType) {
this.resumeFileType = resumeFileType;
}
public DataHandler getResume() {
returnresume;
}
publicvoid setResume(DataHandler resume) {
this.resume = resume;
}
}
服务接口:
@WebService
publicinterface ResumeUploadService {
void uploadResume(@WebParam(name="resume") Resume resume);
}
接口实现类:
@WebService(endpointInterface ="com.webservice.ResumeUploadService",
serviceName ="ResumeService")
publicclass ResumeUploadServiceImplimplements ResumeUploadService {
publicvoid uploadResume(Resume resume) {
DataHandler handler = resume.getResume();
try{
InputStream is = handler.getInputStream();
OutputStream os =new FileOutputStream(
new File("d:\\" + resume.getCandidateName() + "."
+ resume.getResumeFileType()));
byte[] b =new byte[5*1000*1024];//设置附件最大限制
int bytesRead = 0;
while((bytesRead = is.read(b)) != -1){
os.write(b, 0, bytesRead);
}
os.flush();
os.close();
is.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
服务启动类:
publicclass ServerStart {
publicstatic void main(String[] args)throws Exception{
new ServerStart();
System.out.println("Server ready...");
Thread.sleep(5*60*1000);//5分钟后服务端退出
System.out.println("Server exiting");
System.exit(0);
}
protected ServerStart() {
System.out.println("Starting Server...");
ResumeUploadServiceImpl rus =new ResumeUploadServiceImpl();
String address ="http://localhost:9000/mtom0.2/upload";
Endpoint ep = Endpoint.publish(address, rus);
Binding binding = ep.getBinding();
((SOAPBinding)binding).setMTOMEnabled(true);
}
}
客户端启动类:
publicclass ClientStart {
/**
* @paramargs
*/
publicstatic void main(String[] args)throws Exception{
JaxWsProxyFactoryBean factory =new JaxWsProxyFactoryBean();
factory.setServiceClass(ResumeUploadService.class);
factory.setAddress("http://localhost:9000/mtom0.2/upload");
ResumeUploadService client = (ResumeUploadService)factory.create();
Resume resume =new Resume();
int i =new Random().nextInt();
System.out.println("生成的随机数为:" + i);
resume.setCandidateName("cxf" + i);//设置附件名
resume.setResumeFileType("png");//设置附件类型
DataSource source =new FileDataSource(new File("e:\\upload.png"));
resume.setResume(new DataHandler(source));
//测试文件大小
InputStream is = resume.getResume().getInputStream();
System.out.println("文件大小为:" + is.available());
client.uploadResume(resume);//开始上传附件
System.out.println("上传成功!");
System.exit(0);
}
}
2、CXF之MTOM
(原文地址:http://blog.csdn.net/fhd001/archive/2010/08/05/5789311.aspx)
MTOM(消息传输优化机制Message Transmission and Optimization Mechanism)是一个标准的服务,让你的服务传输二进制数据更方便,更有效率。很多框架都支持MTOM---Axis2,JAX-WS RI, JBoss WS , XFire , Microsoft WCF ,等更多.如果二进制是xml文档的一部分,它需要base64编码。当MTOM在一个服务上启用时,它可能需要二进制数据且通常是xml文档的一部分, 并为它创建一个附件。
启用MTOM是一个相当简单的过程。首先,你必须注解POJO类型或schema类型.........下面告诉你怎样为JAXB激活MTOM. MTOM也支持Aegis.
1、 注解
a. 为MTOM修改你的schema
假如我们有这样一个Picture的schema类型,如:
<schema targetNamespace="http://pictures.com"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<element name="Picture">
<complexType>
<sequence>
<element name="Title" type="xsd:string"/>
<element name="ImageData" type="xsd:base64Binary"/>
</sequence>
</complexType>
</element>
</schema>
在这种情况下,ImageData元素是我们想作为附件传送。我们只需要添加一个xmime : expectedContentTypes注解:
<schema targetNamespace="http://pictures.com"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xmime="http://www.w3.org/2005/05/xmlmime">
<element name="Picture">
<complexType>
<sequence>
<element name="Title" type="xsd:string"/>
<element name="ImageData" type="xsd:base64Binary"
xmime:expectedContentTypes="application/octet-stream"/>
</sequence>
</complexType>
</element>
</schema>
这告诉JAXB(其中WSDL2Java用来为你的服务生成POJO),这个字段可以是任何内容类型。而不是为base64Binary创建一个byte[]数组.它将创建一个DataHandler ,而不是用于流数据。
b. 注解你的JAXB bean来启用MTOM
@XmlType
public class Picture {
private String title;
@XmlMimeType("application/octet-stream")
private DataHandler imageData;
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public DataHandler getImageData() { return imageData; }
public void setImageData(DataHandler imageData) { this.imageData = imageData; }
}
2、 在你的服务上启用MTOM
如果你使用JAX-WS API发布你的服务,你可以像这样启用MTOM,如:
import javax.xml.ws.Endpoint;
import javax.xml.ws.soap.SOAPBinding;
Endpoint ep = Endpoint.publish("http://localhost/myService", new MyService());
SOAPBinding binding = (SOAPBinding) ep.getBinding();
binding.setMTOMEnabled(true);
或者,你用xml来发布你的服务:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schema/jaxws.xsd">
<jaxws:endpoint
id="helloWorld"
implementor="demo.spring.HelloWorldImpl"
address="http://localhost/HelloWorld">
<jaxws:properties>
<entry key="mtom-enabled" value="true"/>
</jaxws:properties>
</jaxws:endpoint>
</beans>
如果你使用simple frontend,你可以在ServerFactoryBean或ClientProxyFactoryBean设置mtom-enabled属性.
Map<String,Object> props = new HashMap<String, Object>();
props.put("mtom-enabled", Boolean.TRUE); // Boolean.TRUE or "true" will work as the property value here
ClientProxyFactoryBean pf = new ClientProxyFactoryBean();
pf.setPropertyies(props);
....
YourClient client = (YourClient) pf.create();
ServerFactoryBean sf = new ServerFactoryBean();
sf.setPropertyies(props);
...
sf.create();
你也可以用xml来配置(simple frontend):
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:simple="http://cxf.apache.org/simple"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://cxf.apache.org/simple http://cxf.apache.org/schema/simple.xsd">
<simple:server
id="helloWorld"
serviceClass="demo.spring.HelloWorldImpl"
address="http://localhost/HelloWorld">
<simple:properties>
<entry key="mtom-enabled" value="true"/>
</simple:properties>
</simple:server>
<simple:client
id="helloWorldClient"
serviceClass="demo.spring.HelloWorldImpl"
address="http://localhost/HelloWorld">
<simple:properties>
<entry key="mtom-enabled" value="true"/>
</simple:properties>
</simple:client>
</beans>
3、 使用DataHandlers
Picture picture = ...;
DataHandler handler = picture.getImageData();
InputStream is = handler.getInputStream();
DataSource source = new ByteArrayDataSource(new byte[] {...}, "content/type");
DataSource source = new FileDataSource(new File("my/file"));
Picture picture = new Picture();
picture.setImageData(new DataHandler(source));
4、 大文件上传
上传需要一个实体类(类上需注解XmlAccessorType(XmlAccessType.FIElD)),实例类里有三个参数(candidateName附件名、resumeFileType附件类型、DataHandler resume附件)DataHandler上需注解@XmlMineType(“application/octet-stream”),还需要在服务接口注解@SOAPBinding(style=”SOAPBinding.Style.RPC”)和@MTOM来开启MTOM。
@SOAPBinding(style = SOAPBinding.Style.RPC)指定SOAP消息样式,有两个枚举值:SOAPBinding.Style.DOCUMENT(默认)和 SOAPBinding.Style.RPC,可以对比这两种方式生成的wsdl会有所不同,而且生成的客户端代码也会有所不同。
附件实体类:
package com.pojo;
import javax.activation.DataHandler;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlMimeType;
@XmlAccessorType(XmlAccessType.FIELD)
publicclass Resume {
private StringcandidateName;//附件名
private StringresumeFileType;//附件类型
@XmlMimeType("application/octet-stream")
private DataHandlerresume;//附件
public String getCandidateName() {
returncandidateName;
}
publicvoid setCandidateName(String candidateName) {
this.candidateName = candidateName;
}
public String getResumeFileType() {
returnresumeFileType;
}
publicvoid setResumeFileType(String resumeFileType) {
this.resumeFileType = resumeFileType;
}
public DataHandler getResume() {
returnresume;
}
publicvoid setResume(DataHandler resume) {
this.resume = resume;
}
}
接口类:
package com.webservice;
import java.io.IOException;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
importjavax.xml.ws.soap.MTOM;
import com.pojo.Resume;
@WebService
@SOAPBinding(style=SOAPBinding.Style.RPC)
@MTOM
publicinterface ResumeUploadService {
void uploadResume(@WebParam(name="resume") Resume resume) throws IOException;
}
接口实现类:
package com.webservice;
import java.io.File;
importjava.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.activation.DataHandler;
import javax.jws.WebService;
import com.pojo.Resume;
@WebService(endpointInterface ="com.webservice.ResumeUploadService",
serviceName ="ResumeService")
//@BindingType(SOAPBinding.SOAP11HTTP_MTOM_BINDING)
publicclass ResumeUploadServiceImplimplements ResumeUploadService {
publicvoid uploadResume(Resume resume)throws IOException{
DataHandler handler = resume.getResume();
finalint BUFFER_SIZE = 50*1024*1024;//缓冲区大小为3M
long fileLength = 0;
int read = 0;
InputStream is =null;
OutputStream os =null;
try{
//1.
is = handler.getInputStream();
os =new FileOutputStream(
new File("d:\\" + resume.getCandidateName() + "."
+ resume.getResumeFileType()));
// is = new BufferedInputStream(handler.getInputStream());
// os = new BufferedOutputStream(new FileOutputStream("d:\\" + resume.getCandidateName() + "."
// + resume.getResumeFileType()));
fileLength = is.available();
System.out.println("服务端测试文件大小:" + fileLength);
byte[] b =new byte[BUFFER_SIZE];
while((read = is.read(b)) != -1){
os.write(b, 0, read);
}
//2.目前测试6M文件可以上传,10M内存溢出
//
// byte[] dst = new byte[BUFFER_SIZE];//每次读出3M
// for(int offset=0; offset<fileLength; offset+=BUFFER_SIZE){
// if(fileLength - offset >= BUFFER_SIZE){
// read = is.read(dst);
// os.write(dst, 0, read);
//System.out.println("test if run " + offset);
// }else{
// read = is.read(dst);
// os.write(dst, 0, read);
//System.out.println("test else run");
// }
// read=0;
// os.flush();
// }
}catch(IOException e){
e.printStackTrace();
}finally{
if(is !=null)is.close();
if(os !=null){
os.flush();
os.close();
}
}
}
}
服务端启动类:
package com.server;
import javax.xml.ws.Endpoint;
publicclass ServerStart {
/**
* @paramargs
* 上传附件的时候应注意把MyException自带的JavaEE 5 Libraries删掉,
* 否则会出现Exceptionin thread "main"java.lang.NoClassDefFoundError:com/sun/mail/util/LineInputStream
* 原因:JavaEE版本和JavaMail的版本不一致。
*/
publicstatic void main(String[] args)throws Exception{
new ServerStart();
System.out.println("Server ready...");
Thread.sleep(5*60*1000);//5分钟后服务端退出
System.out.println("Server exiting");
System.exit(0);
}
protected ServerStart() {
System.out.println("Starting Server...");
ResumeUploadServiceImpl rus =new ResumeUploadServiceImpl();
String address ="http://localhost:9000/mtom0.2/upload";
Endpoint.publish(address, rus);
// Endpoint ep = Endpoint.publish(address, rus);
// Binding binding = ep.getBinding();
// System.out.println(binding.getBindingID());
// ((SOAPBinding)binding).setMTOMEnabled(true);
}
}
客户端启动类:
package com.client;
import java.io.File;
import java.io.InputStream;
import java.util.Random;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import com.pojo.Resume;
import com.webservice.ResumeUploadService;
publicclass ClientStart {
/**
* @paramargs
*/
publicstatic void main(String[] args)throws Exception{
JaxWsProxyFactoryBean factory =new JaxWsProxyFactoryBean();
factory.setServiceClass(ResumeUploadService.class);
factory.setAddress("http://localhost:9000/mtom0.2/upload");
ResumeUploadService client = (ResumeUploadService)factory.create();
Resume resume =new Resume();
//启动MTOM(有问题)
// QName qname = new QName("http://webservice.com/", "ResumeService");
// Service svc = Service.create(qname);
// ResumeUploadService proxy = svc.getPort(ResumeUploadService.class);
// BindingProvider bp = (BindingProvider) proxy;
// SOAPBinding binding = (SOAPBinding) bp.getBinding();
// binding.setMTOMEnabled(true);
int i =new Random().nextInt();
System.out.println("生成的随机数为:" + i);
resume.setCandidateName("cxf" + i);//设置附件名
DataSource source =new FileDataSource(new File("e:\\myTools\\jdk-1_5_0-windows-i586.exe"));
DataHandler handler =new DataHandler(source);
resume.setResume(handler);//设置附件
//得到附件类型
StringfileType = source.getName().substring(source.getName().lastIndexOf(".")+1);
System.out.println("文件类型为:" + fileType);
resume.setResumeFileType(fileType);//设置附件类型
//测试文件大小
InputStream is = resume.getResume().getInputStream();
System.out.println("文件大小为:" + is.available() + "个字节");
client.uploadResume(resume);//开始上传附件
System.out.println("上传成功!");
System.exit(0);
}
}
3、 CXF之WS-Security安全机制
一个WebService挂在互联网上,如果知道地址,那么任何人都可以访问,所以应该考虑 WebService的安全问题。应该只让指定的客户端访问。下面利用Jax-WS的拦截器来做用户验证。
和上面例子一样,首先需要一个服务接口和服务实现类,这里省略。
其次需要2个身份验证的CallbackHandler:
服务端CallbackHandler
package com.mms.webservice.test;
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
publicclass ServerPasswordCallbackimplements CallbackHandler {
publicvoid handle(Callback[] callbacks)throws IOException,
UnsupportedCallbackException {
WSPasswordCallback pwcb = (WSPasswordCallback) callbacks[0];
String id = pwcb.getIdentifier();
switch (pwcb.getUsage()) {
case WSPasswordCallback.USERNAME_TOKEN_UNKNOWN:
// 密码方式 plaintext
if (!"cxf".equals(id)) {
thrownew UnsupportedCallbackException(pwcb,
"username is invalid.");
}
if (!"mima".equals(pwcb.getPassword())) {
thrownew UnsupportedCallbackException(pwcb,
"password is invalid.");
}
break;
case WSPasswordCallback.DECRYPT:
case WSPasswordCallback.SIGNATURE:
// used to retrieve password for private key
if ("serverkey".equals(id)) {
pwcb.setPassword("serverpass");
}
break;
}
}
}
客户端CallbackHandler:
package com.mms.webservice.test;
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
publicclass ClientPasswordCallbackimplements CallbackHandler {
publicvoid handle(Callback[] callbacks)throws IOException, UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
//服务器标识
System.out.println("Server ID:"+pc.getIdentifier());
// 设置返回给客户的服务器密码信息
pc.setPassword("mima");
pc.setIdentifier("cxf");
}
}
然后还需要服务端启动类和和客户端启动类:
服务端启动类:
package com.server;
import java.util.HashMap;
import java.util.Map;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
import org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.handler.WSHandlerConstants;
import com.mms.webservice.HelloWorldImpl;
import com.mms.webservice.test.ServerPasswordCallback;
publicclass Server {
/**
* @paramargs
*/
publicstatic void main(String[] args) {
Server server =new Server();
//server.test();//没有安全验证WS
server.testUsernameToken();//有安全验证WS
}
privatevoid test() {
JaxWsServerFactoryBean server =new JaxWsServerFactoryBean();
// server.getInInterceptors().add(new LoggingInInterceptor());
// server.getOutInterceptors().add(new LoggingOutInterceptor());
server.setServiceClass(HelloWorldImpl.class);
server.setAddress("http://localhost:9000/security");
server.create();
}
private WSS4JInInterceptor getInInterceptor() {
Map<String, Object> inProps =new HashMap<String, Object>();
inProps.put(WSHandlerConstants.ACTION,
WSHandlerConstants.USERNAME_TOKEN);
// 密码类型 : plain text
inProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
// 客户信息校验类设置
inProps.put(WSHandlerConstants.PW_CALLBACK_CLASS,
ServerPasswordCallback.class.getName());
returnnew WSS4JInInterceptor(inProps);
}
privatevoid testUsernameToken() {
JaxWsServerFactoryBean server =new JaxWsServerFactoryBean();
//将回调拦截器加入server拦截器
server.getInInterceptors().add(getInInterceptor());
//server.getOutInterceptors().add(getOutInterceptor());
server.setServiceClass(HelloWorldImpl.class);
server.setAddress("http://localhost:9000/security");
server.create();
}
}
客户端启动类:
package com.client;
import java.util.HashMap;
import java.util.Map;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.handler.WSHandlerConstants;
import com.mms.webservice.HelloWorld;
import com.mms.webservice.test.ClientPasswordCallback;
publicclass client {
/**
* @paramargs
*/
publicstatic void main(String[] args) {
JaxWsProxyFactoryBean factory =new JaxWsProxyFactoryBean();
//打印日志
factory.getOutInterceptors().add(new LoggingInInterceptor());
factory.getInInterceptors().add(new LoggingOutInterceptor());
factory.getOutInterceptors().add(getOutInterceptor());
factory.setAddress("http://localhost:9000/security");
factory.setServiceClass(HelloWorld.class);
HelloWorld client = (HelloWorld)factory.create();
System.out.println(client.sayHi("cxf"));
}
privatestatic WSS4JOutInterceptor getOutInterceptor() {
Map<String, Object> outProps =new HashMap<String, Object>();
outProps.put(WSHandlerConstants.ACTION,
WSHandlerConstants.USERNAME_TOKEN);
// 服务器用户标识
outProps.put(WSHandlerConstants.USER,"IamServer");
// 密码类型 : plain text
outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
// 返回给客户端的密码信息
outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS,
ClientPasswordCallback.class.getName());
returnnew WSS4JOutInterceptor(outProps);
}
}
原理:利用CXF自身的拦截器
参考文章:http://hi.baidu.com/leezuu/blog/item/e502c3ae3de7da044a36d675.html
4、 CXF之JSON应用
下面例子展示如何在ServerFactoryBeans应用JSON,首先创建一个HashMap属性来设置StAX的XMLInputFactory和XMLOutputFactory:
Map<String,Object> properties = new HashMap<String,Object>();
// Create a mapping between the XML namespaces and the JSON prefixes.
// The JSON prefix can be "" to specify that you don't want any prefix.
HashMap<String, String> nstojns = new HashMap<String,String>();
nstojns.put("http://customer.acme.com", "acme");
MappedXMLInputFactory xif = new MappedXMLInputFactory(nstojns);
properties.put(XMLInputFactory.class.getName(), xif);
MappedXMLOutputFactory xof = new MappedXMLOutputFactory(nstojns);
properties.put(XMLOutputFactory.class.getName(), xof);
设置Context-Type属性:
// Tell CXF to use a different Content-Type for the JSON endpoint
// This should probably be application/json, but text/plain allows
// us to view easily in a web browser.
properties.put("Content-Type", "text/plain");
最后创建服务:
// Build up the server factory bean
JaxWsServerFactoryBean sf = new JaxWsServerFactoryBean();
sf.setServiceClass(CustomerService.class);
// Use the HTTP Binding which understands the Java Rest Annotations
sf.setBindingId(HttpBindingFactory.HTTP_BINDING_ID);
sf.setAddress("http://localhost:8080/json");
sf.setServiceBean(new CustomerServiceImpl());
sf.setProperties(properties);
sf.create();
原文来自Apache官网:http://cxf.apache.org/docs/json-support.html
5、 CXF之嵌入Jetty
这次只介绍服务端,嵌入Jetty不需要导入Jetty的jar包,只需要导入CXF的包即可(CXF已把Jetty整合)。
首先的创建服务接口和服务实现类,这里不再重复,参照前面例子。
其次创建一个Jetty启动类,用来启动Jetty并且发布Web服务。
package com.server;
import javax.xml.ws.Endpoint;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import com.service.HelloWorldImpl;
publicclass JettyServer {
publicstatic void main(String[] args){
new JettyServer();
}
private JettyServer(){
System.out.println(System.getProperty("user.dir"));
String app =System.getProperty("user.dir");//得到项目路径
org.eclipse.jetty.server.Server server
=new org.eclipse.jetty.server.Server();//新建一个Jetty服务器
Connector conn =new SelectChannelConnector();//新建Connector组件
conn.setHost("127.0.0.1");
conn.setPort(9000);
server.setConnectors(new Connector[]{conn});//插入服务器主件中
org.eclipse.jetty.server.handler.ContextHandler context
=new org.eclipse.jetty.server.handler.ContextHandler();//新建Handler组件
context.setContextPath("/");//Web应用的上下文跟路径
context.setResourceBase(app);
server.setHandler(context);//插入服务器主件中
org.eclipse.jetty.util.thread.QueuedThreadPool pool
=new org.eclipse.jetty.util.thread.QueuedThreadPool();//创建线程池组件
pool.setMaxThreads(100);//设置最大线程数
pool.setMinThreads(10);//设置最小线程数
server.setThreadPool(pool);
//发布Web服务
//Endpoint.publish("http://127.0.0.1:9000/WS-CXF-Jetty", new HelloWorldImpl());
JaxWsServerFactoryBean wsServer =new JaxWsServerFactoryBean();
wsServer.setAddress("http://127.0.0.1:9000/WS-CXF-Jetty");
wsServer.setServiceBean(new HelloWorldImpl());
wsServer.create();
try {
server.start();
//System.out.println(server.isStarted());
}catch (Exception e) {
e.printStackTrace();
}
}
}
参考文档:http://liuskysun.blog.163.com/blog/static/9981297820101023139723/
5、 关于wsdl2java自动生成客户端
利用wsdl2java生成的客户端,有个继承javax.xml.ws.Service的类,此方法的3个构造函数会报错(The constructor Service(URL, QName, WebServiceFeature[]) is undefined),原因是:jdk中有个javax.xml.ws.Service(低版本),而CXF的geronimo-jaxws_2.2_spec-1.0.jar中也有个javax.xml.ws.Service(高版本),而类里默认调用的是jdk(低版本)的Service,故报错!