java创建webservice client

java的webservice实现有多种方式,可用的工具也有一些。之前对这块的只是比较缺乏,以至于一上来就一直看spring webservice.花费了几天后发现和要用的功能不符,就···

当前学习的需求是webservice client。因此整篇文章用来说明java webserviceclient的创建过程。

首先使用java自带的soapconnection实现。那首先具体的client访问流程为

SOAPConnection connection = null;

		try {
			SOAPConnectionFactory sfc = SOAPConnectionFactory.newInstance();
			connection = sfc.createConnection();
			SOAPMessage soapMessage = ObjectToSoapXml(object, nsMethod, nsName);

			URL endpoint = new URL(new URL(url),
					"",
					new URLStreamHandler() {
						@Override
						protected URLConnection openConnection(URL url) throws IOException {
							URL target = new URL(url.toString());
							URLConnection connection = target.openConnection();
							// Connection settings
							connection.setConnectTimeout(120000); // 2 min
							connection.setReadTimeout(60000); // 1 min
							return(connection);
						}
					});

			SOAPMessage response = connection.call(soapMessage, endpoint);
			
		} catch (Exception e) {
			
		}

 这其中首先创建soapconnection调用call方法向server端发送请求,call的两个参数一个是发送的消息soapmessage,一个是服务器端地址。

那这里的关键是soapmessage的封装,那在java中,信息一般采用对象的形式存储。问题就是怎样把含有信息的对象封装成soapMessage.我采用的方法是

private static<T> SOAPMessage ObjectToSoapXml(T object, String nsMethod, String nsName) {
		SOAPMessage soapMessage = null;

		try {
			MessageFactory messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
			soapMessage = messageFactory.createMessage();
			SOAPPart soapPart = soapMessage.getSOAPPart();
			// SOAP Envelope
			SOAPEnvelope envelope = soapPart.getEnvelope();
			envelope.setPrefix("SOAP-ENV");
			envelope.addNamespaceDeclaration("ns1", nsMethod);

			// SOAP Body
			SOAPBody soapBody = envelope.getBody();
			soapBody.setPrefix("SOAP-ENV");

			soapBody.addDocument(jaxbObjectToXML(object, nsMethod, nsName));//将body中的类通过document的形式写入
			soapMessage.saveChanges();
		} catch (SOAPException e) {
			e.printStackTrace();
			
		}

		return soapMessage;
	}

  使用messagefactory创建soapmessage,这里有一点要注意SOAPConstants.SOAP_1_1_PROTOCOL,使用这个参数的原因是要指定soapmessage的content-type为text/xml,charset=utf-8,那其他的可再参考其他常量。那创建的soapmessage就包含soapenvelop了,可添加前缀和命名空间。接下来就是在soapbody中添加要传递的信息对象。一开始看到的各种例子都是一个元素一个元素的添加。可扩展性太差了。一直考虑将整个对象添加进去。采用方式是soapbody adddocument的方式。那就要把信息对象转换成org.w3c.dom.Document对象。接下来是转换方式

private static<T> Document jaxbObjectToXML(T emp, String nsMethod, String nsName) {
		try {
			JAXBContext context = JAXBContext.newInstance(emp.getClass());
			// Create the Document
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();
			Document document = db.newDocument();

			// Marshal the Object to a Document
			Marshaller marshaller = context.createMarshaller();
			marshaller.marshal(emp, document);
			if(null != document) {
				document.renameNode(document.getFirstChild(), nsMethod, nsName);
			}

			return document;
		} catch (Exception e) {
			logger.error(e.toString(), e);
		}
		return null;
	}

  使用jaxb将object转成xml,marshal方法可直接实现对象到document。我在这里遇到的一个问题是,对象到xml的时候,我的xml要求根元素有前缀。没知识实在不好添加。最终找到实现方式是转换成的document获取根元素,通过getfirstchild的方式,然后对根元素重命名 renameNode,这里的问题是这个方法的后两个参数一个是命名空间,一个是重命名后节点名称。我要使用的是含有前缀,无命名空间。其实这样说就是没知识了。前缀和命名空间应该是对应的,命名空间和前缀应一起设置。只是命名空间并不显示。若只设置前缀,无命名空间则会报错。那这里问题就愉快的解决了,此时是完成了对象封装成soapmessage,就可以通过soapconnection向服务端发消息了。

那消息发送出去服务端返回结果是不是还要处理一下呢?当然可以通过元素逐级获取的方式获取你要的元素。同样扩展性太差。我采用的方式同样是把soapbody中的内容实现到对象的对应。

public static<T> T parseSoapMessage(SOAPMessage reqMsg, T object, String name) {
		try {
			reqMsg = removeUTFBOM(reqMsg);
			SOAPBody soapBody = reqMsg.getSOAPBody();
			Document document = soapBody.extractContentAsDocument();//获取返回信息中的消息体
			document.renameNode(document.getFirstChild(), null, name);//根节点去掉前缀
			JAXBContext jc = JAXBContext.newInstance(object.getClass());
			Unmarshaller unmarshaller = jc.createUnmarshaller();
			object = (T)unmarshaller.unmarshal(document);
		}catch(Exception e) {
			logger.error(e.toString(), e);
		}

		return object;
	}

  

大问题来了,调用soapconnection返回的soapmessage,直接调用getsoapbody报错了。几番查看,是返回的结果是utf-8 BOM滴,额,这个处理没有找到好的方式。最终也只是将soapmessage转成string将BOM去掉之后再转换回来。因之前对象到soapmessage转换时使用document,那现在也考虑这种方式并且可行了。那注意抽取出来的document呢我这边还是含有前缀的,所以使用renameNode做了一下去除前缀的处理,然后使用unmarshal将document嗨皮的转成对象了。终于完成了。

去BOM的方式

private static SOAPMessage removeUTFBOM(SOAPMessage soapMessage) {
		ByteArrayOutputStream baos = null;
		try
		{
			baos = new ByteArrayOutputStream();
			soapMessage.writeTo(baos);
			String soapString = baos.toString();
			if (baos.toString().startsWith("\uFEFF")) {
				soapString = soapString.substring(1);
				InputStream is = new ByteArrayInputStream(soapString.getBytes());
				soapMessage = MessageFactory.newInstance().createMessage(null, is);
			}

		} catch (SOAPException e) {
			logger.error(e.toString(), e);
		} catch (IOException e) {
			logger.error(e.toString(), e);
		}

		return soapMessage;
	}

  最后还有一点就是xml对应bean的定义

我采取的方式是在类上注解

@XmlRootElement(name = "name") //声明为根元素,根元素名字
@XmlAccessorType(XmlAccessType.FIELD)

然后在各个元素上

@XmlElement(name = "elementname", nillable = true)

指定该属性对应xml中元素的名字,使用nillable属性是因为,若此属性为空的话,相应的xml元素便不存在,指定此属性为true,则为空的属性也会显示。

再就是为根元素的类中含有其他对象时,其他对象的声明方式

首先在类上声明   @XmlAccessorType(XmlAccessType.FIELD)

相应属性上声明      @XmlElement(name = "elementname", nillable = true)

再就是有些属性为list

@XmlElementWrapper(name="ListName")
@XmlElement(name="ListelementName", nillable = true)

wrapper指定list的名字,接下来指定list中各个元素的名字。

呼~ 终于走完了

posted @ 2015-06-19 17:49  ayouyj  阅读(6173)  评论(0编辑  收藏  举报