1.3?一个简单的WebService运行实例
WebService代码:
package?stock.server; @javax.jws.WebService public?class?StockQuoteProvider?{ ???public?StockQuoteProvider () { ???} ???float?getLastTradePrice (String tickerSymbol) { ??????return?"abc".equals(tickerSymbol)? 1234.0f : 0.0f; ????} } |
客户端代码:
import?java.lang.annotation.Annotation; import?stock.server.*; import?javax.xml.ws.Service; ? class?StockQuoteClient { ??????static?void?main(String[] args)?throwsException { ??????StockQuoteProviderService service =?newStockQuoteProviderService(); ??????StockQuoteProvider port = service.getStockQuoteProviderPort(); ???????????System.out.println(port.getLastTradePrice(args[0])); ??????} }
?
?
2.?Web?Service技术研究的环境准备
2.1?安装运行环境
???推荐安装GlassFish服务器,具体安装方法可以参见以下文章:
《手把手教你?怎么?安装?GlassFish》 http://blog.csdn.net/nanjingjiangbiao/archive/2010/01/28/5264913.aspx |
2.2?下载WebService API(JAX-WS)的源代码
???具体的下载URL参见以下地址:
https://jax-ws-sources.dev.java.net/source/browse/jax-ws-sources/ |
2.3?部署服务器端
1.把1.3中的服务器端部署到GlassFish中
2.用wsimport命令生成stub中间程序
3.把1.3中的客户端代码和stub代码,以及2.2下载的WebService源代码部署到Eclipse工程中,就可以DEBUG了。
具体的可以参照以下文章:
SOA技术研究之?图解JAX-WS术》 http://blog.csdn.net/nanjingjiangbiao/archive/2010/02/10/5305049.aspx
3.?Web?Service的技术内幕
3.1?客户端是如何和WSDL建立关系的?
???1.?首先,我们看到客户端程序中有以下这样一行
???StockQuoteProviderService?service =?new ?StockQuoteProviderService(); |
???2.?这行代码,会调用到
public?StockQuoteProviderService() { ????????super(STOCKQUOTEPROVIDERSERVICE_WSDL_LOCATION,?newQName("http://server.stock/",?"StockQuoteProviderService")); ????}
3.?这行代码,会调用到com.sun.xml.ws.spi.ProviderImpl的
@Override ????public?ServiceDelegate createServiceDelegate( URL wsdlDocumentLocation,QName serviceName,?Class?serviceClass) { ?????????return?new?WSServiceDelegate(wsdlDocumentLocation,serviceName,?serviceClass); ????} |
4.?这行代码,会调用到com.sun.xml.ws.clientWSServiceDelegate的
/** ?????*?@param?serviceClass ?????*??????Either?{@link Service}.class?or?other?generatedservice-derived?classes. ?????*/ ????public?WSServiceDelegate(@Nullable?Source wsdl,?@NotNullQName serviceName,100)">@NotNull?final?Class<??extends?Service> serviceClass) { ???????~省略~ ????} |
5.?而其中最关键的就是以下这段,解析阅读WSDL的代码,这段代码主要是用XMLStream来构建WSDL代码,不足为奇。
WSDLServiceImpl?service=null; ????????if?(wsdl !=?null) { ????????????try?{ ????????????????URL url = wsdl.getSystemId()==null???null?:?newURL(wsdl.getSystemId()); ????????????????WSDLModelImpl model = parseWSDL(url,wsdl); ????????????????service = model.getService(this.serviceName); ????????????????if?(service ==?null) ????????????????????throw?new?WebServiceException( ???????????????????????ClientMessages.INVALID_SERVICE_NAME(serviceName, ???????????????????????????buildNameList(model.getServices().keySet()))); ????????????????// fill in statically known ports ????????????????for?(WSDLPortImpl port : service.getPorts()) ????????????????????ports.put(port.getName(),85)">new?PortInfo(this,port)); ????????????}?catch?(MalformedURLException e) { ????????????????newWebServiceException(ClientMessages.INVALID_WSDL_URL(wsdl.getSystemId()),e); ????????????} ????????} ????????wsdlService?= service; |
3.2?客户端是如何和SEI建立关系的?
1.?首先,我们看到客户端程序中有以下这样一行
StockQuoteProvider port = service.getStockQuoteProviderPort(); |
2.?这行代码,会调用到com.sun.xml.ws.clientWSServiceDelegate的
private?<T> T?getPort(WSEndpointReference wsepr,QName portName,Class<T> portInterface, ??????????????????????????WebServiceFeature... features) { ????????SEIPortInfo spi = addSEI(portName,portInterface,features); ????????returncreateEndpointIFBaseProxy(wsepr,portName,features,spi); ????}
3.?这行代码,会调用到com.sun.xml.ws.clientWSServiceDelegate的
*?Creates?a?new?pipeline?for?the?given?port?name. ?????private?Tube?createPipeline(PortInfo portInfo,WSBinding binding) { ????????//Check all required WSDL extensions are understood ????????checkAllWSDLExtensionsUnderstood(portInfo,binding); ????????SEIModel seiModel =?if(portInfo?instanceof?SEIPortInfo) { ????????????seiModel = ((SEIPortInfo)portInfo).model; ????????} ????????BindingID bindingId = portInfo.bindingId; ????????TubelineAssembler assembler =TubelineAssemblerFactory.create( ????????????????Thread.currentThread().getContextClassLoader(),bindingId); ????????if?(assembler ==?null) ????????????new?WebServiceException("Unable to process bindingID="?+ bindingId);????//?TODO: i18n ????????return?assembler.createClient( ????????????????new?ClientTubeAssemblerContext( ????????????????????????portInfo.targetEndpoint, ????????????????????????portInfo.portModel, ????????????????????????container,((BindingImpl)binding).createCodec(),seiModel)); ????}
在这段代码中,使用了TUBE技术,把客户端和服务器之间建立了关系。
4.?这行代码,会调用到com.sun.xml.ws.util.pipe.StandaloneTubeAssembler的
@NotNull ????public?Tube createClient(ClientTubeAssemblerContext context) { ????????Tube head = context.createTransportTube(); ????????head = context.createSecurityTube(head); ????????if?(dump) { ????????????// for debugging inject a dump pipe. this is left in the production code, ????????????// as it would be very handy for a trouble-shooting at the production site. ????????????head = context.createDumpTube("client",System.out,head); ????????} ????????head = context.createWsaTube(head); ????????head = context.createClientMUTube(head); ????????head = context.createValidationTube(head); ????????return?context.createHandlerTube(head);??????? ????} ?
在以上代码中,开始和SOAP绑定,准备发送请求和调用函数等等。
?
3.3?客户端是如何发送请求的?
port.getLastTradePrice(args[0]) |
|
2.?这行代码,会调用到com.sun.xml.ws.client.sei.SEIStub的
public?Object invoke(Object proxy,Method method,Object[] args)throws?Throwable { ????????MethodHandler handler =?methodHandlers.get(method);
????????if?(handler !=?return?handler.invoke(proxy,args);
????????}?else?{
????????????// we handle the other method invocations by ourselves
????????????try?{
????????????????return?method.invoke(????????????}?catch?(IllegalAccessException e) {
????????????????// impossible
????????????????new?AssertionError(e);
????????????}?catch?(IllegalArgumentException e) {
????????????????catch?(InvocationTargetException e) {
????????????????throw?e.getCause();
????????????}
????????}
????}
3.?这行代码,会调用到com.sun.xml.ws.client.stub的(中间省去了2层不重要的代码)
Passes?message?to?pipe?processing. ?????*?<p>
?????Unlike?{@link Tube}?instances,
?????this?method?is?threadsafe?and?can?be?invoked?from
?????multiple?threads?concurrently.
?????*
?????packet?????????The?sent?theserver
?????requestContext?{@link RequestContext}?wheninvocation?originally?scheduled.
?????*???????????????????????This?must?same?object?as{@link #requestContext}?synchronous
?????invocations,?but?asynchronousit?needs?snapshot
?????captured?at?point?ofinvocation,191)">correctly?satisfy?spec?requirement.
?????receiver???????Receives?{@link ResponseContext}.?Since?requires
?????that?asynchronous?invocationsnot?update?response?context,191)">depending?on?mode?of?invocationthey?have?go?different?places.
?????So?we?take?setter?abstractsaway.
?????protected?final?Packet process(Packet packet,RequestContext requestContext,ResponseContextReceiver receiver) {
????????configureRequestPacket(packet,requestContext);
????????Pool<Tube> pool =?tubes;
????????if?(pool ==?"close method has already been invoked");?: i18n
?
????????Fiber fiber =?engine.createFiber();
????????// then send it away!
????????Tube tube = pool.take();
?
????????try?{
????????????return?fiber.runSync(tube,packet);
????????}?finally?{
????????????// this allows us to capture the packet even when the call failed with an exception.
????????????// when the call fails with an exception it's no longer a 'reply' but it may provide some information
????????????// about what went wrong.
?
????????????// note that Packet can still be updated after
????????????// ResponseContext is created.
????????????Packet reply = (fiber.getPacket() ==?null) ? packet : fiber.getPacket();
????????????receiver.setResponseContext(newResponseContext(reply));
?
????????????pool.recycle(tube);
????????}
????}
4.?这行代码,会调用到com.sun.xml.ws.api.pipe.?__doRun的(中间省去了3层不重要的代码)
这段代码开始发送打成数据包的http请求
To?from?{@link #doRun(Tube)}. ?????@see?#doRun(Tube)
?????private?Tube __doRun(Tube next) {
????????final?Fiber old =?CURRENT_FIBER.get();
????????CURRENT_FIBER.set(this);
?
????????// if true,lots of debug messages to show what's being executed
????????final?boolean?traceEnabled =LOGGER.isLoggable(Level.FINER);
?
????????while(!isBlocking() && !needsToReenter) {
????????????????try?{
????????????????????NextAction na;
????????????????????Tube last;
????????????????????if(throwable!=null) {
????????????????????????contsSize==0) {
????????????????????????????// nothing else to execute. we are done.
????????????????????????????null;
????????????????????????}
????????????????????????last = popCont();
????????????????????????if(traceEnabled)
????????????????????????????LOGGER.finer(getName()+' '+last+".processException("+throwable+')');
????????????????????????na = last.processException(throwable);
????????????????????}?else?{
????????????????????????if(next!=null) {
????????????????????????????if(traceEnabled)
????????????????????????????????' '+next+".processRequest("+packet+')');
????????????????????????????na = next.processRequest(packet);
????????????????????????????last = next;
????????????????????????}?else?{
????????????????????????????contsSize==0) {
????????????????????????????????// nothing else to execute. we are done.
????????????????????????????????null;
????????????????????????????}
????????????????????????????last = popCont();
????????????????????????????".processResponse("+')');
????????????????????????????na = last.processResponse(packet);
????????????????????????}
????????????????????}
?~省略~
5.?这行代码,会调用到com.sun.xml.ws.encoding.?StreamSOAPCodec的(中间省去了无数层不重要的代码)的
public?ContentType encode(Packet packet,OutputStream out) { ????????if?(packet.getMessage() !=?null) {
????????????XMLStreamWriter writer = XMLStreamWriterFactory.create(out);
????????????try?{
????????????????packet.getMessage().writeTo(writer);
????????????????writer.flush();
????????????}?catch?(XMLStreamException e) {
????????????????new?WebServiceException(e);
????????????}
????????????XMLStreamWriterFactory.recycle(writer);
????????}
????????return?getContentType(packet.soapAction);
????}
大家可以看到,他发出请求了。
3.4?服务器端是如何接受请求的?
1.?首先,服务器端有一个名叫JAXWSServlet的Servlet常驻服务器,监听请求。所以,请求会首先被转发给com.sun.enterprise.webservice.JAXWSServlet的
????void?doPost(HttpServletRequest request, ??????????????????????????HttpServletResponse response) ????????throws?ServletException,IOException{ ?????????/** ?????????requirement?came?jbi?team.?If?WebServiceEndpoint ?????????endpoint?which?private?throw?an?errorwhenever?get ?????????post?request?made ?????????*/ ????????Endpoint endpt = ?wsEngine_.getEndpoint(request.getServletPath()); ~省略~ |
2.最后,代码会调转到以下com.sun.xml.ws.transport.http.HttpAdapter的
class?HttpToolkit?extends?Adapter.Toolkit?{ ????????void?handle(WSHTTPConnection con)?throws?IOException {
????????????boolean?invoke =?false;
????????????try?{
????????????????Packet packet =?new?Packet();
????????????????try?{
????????????????????packet = decodePacket(con,192)">codec);
????????????????????invoke =?true;
????????????????}?catch(ExceptionHasMessage e) {
????????????????????LOGGER.log(Level.SEVERE,255)">"JAXWS2015: An ExceptionHasMessage occurred. "?+ e.getMessage(),e);
????????????????????packet.setMessage(e.getFaultMessage());
????????????????}?catch(UnsupportedMediaException e) {
????????????????????"JAXWS2016: An UnsupportedMediaException occurred. "?+ e.getMessage(),e);
????????????????????con.setStatus(WSHTTPConnection.UNSUPPORTED_MEDIA);
????????????????}?catch(Exception e) {
????????????????????"JAXWS2017: A ServerRtException occurred. "?+ e.getMessage(),e);
???????????????????con.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR);
????????????????}
????????????????if?(invoke) {
????????????????????try?{
????????????????????????packet =?head.process(packet,con.getWebServiceContextDelegate(),
????????????????????????????????packet.transportBackChannel);
????????????????????}?catch(Exception e) {
????????????????????????"JAXWS2018: An Exception occurred. "?+ e.getMessage(),e);
????????????????????????if?(!con.isClosed()) {
????????????????????????????writeInternalServerError(con);
????????????????????????}
????????????????????????return;
????????????????????}
????????????????}
???????????????encodePacket(packet,con,192)">codec);
????????????}?finally?{
????????????????if?(!con.isClosed()) {
????????????????????con.close();
????????????????}
????????????}
????????}
????}
以上代码分为三块大的处理,分别是
packet = decodePacket(con,192)">codec);
packet =?????????????????????????????????packet.transportBackChannel);
encodePacket(packet,192)">codec);
用来解析数据包,调用服务,发送回复数据包。这里就不详细介绍了。
4.?总结
希望能够通过这样的文章,更清晰的,直白的把WebService的详细技术内幕公布给大家,为开源软件运动作一份自己的贡献。