WebService代码:
package stock.server; @javax.jws.WebService public class StockQuoteProvider { public StockQuoteProvider () { } public 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;
public class StockQuoteClient { public static void main(String[] args) throwsException { StockQuoteProviderService service = newStockQuoteProviderService(); StockQuoteProvider port = service.getStockQuoteProviderPort(); System.out.println(port.getLastTradePrice(args[0])); } } |
推荐安装GlassFish服务器,具体安装方法可以参见以下文章:
《手把手教你 怎么 安装 GlassFish》 http://blog.csdn.net/nanjingjiangbiao/archive/2010/01/28/5264913.aspx |
具体的下载URL参见以下地址:
https://jax-ws-sources.dev.java.net/source/browse/jax-ws-sources/ |
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 |
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, @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(this.serviceName, buildNameList(model.getServices().keySet()))); // fill in statically known ports for (WSDLPortImpl port : service.getPorts()) ports.put(port.getName(), new PortInfo(this, port)); } catch (MalformedURLException e) { throw newWebServiceException(ClientMessages.INVALID_WSDL_URL(wsdl.getSystemId()), e); } } this.wsdlService = service; |
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,portInterface,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 = null; if(portInfo instanceof SEIPortInfo) { seiModel = ((SEIPortInfo)portInfo).model; } BindingID bindingId = portInfo.bindingId; TubelineAssembler assembler =TubelineAssemblerFactory.create( Thread.currentThread().getContextClassLoader(), bindingId); if (assembler == null) throw new WebServiceException("Unable to process bindingID=" + bindingId); // TODO: i18n return assembler.createClient( new ClientTubeAssemblerContext( portInfo.targetEndpoint, portInfo.portModel, this, binding, 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绑定,准备发送请求和调用函数等等。
1. 首先,我们看到客户端程序中有以下这样一行
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 != null) { return handler.invoke(proxy, args); } else { // we handle the other method invocations by ourselves try { return method.invoke(this, args); } catch (IllegalAccessException e) { // impossible throw new AssertionError(e); } catch (IllegalArgumentException e) { throw new AssertionError(e); } catch (InvocationTargetException e) { throw e.getCause(); } } } |
3. 这行代码,会调用到com.sun.xml.ws.client.stub的(中间省去了2层不重要的代码)
/** * Passes a message to a pipe for processing. * <p> * Unlike {@link Tube} instances, * this method is thread-safe and can be invoked from * multiple threads concurrently. * * @param packet The message to be sent to theserver * @param requestContext The {@link RequestContext} whenthis invocation is originally scheduled. * This must be the same object as{@link #requestContext} for synchronous * invocations, but for asynchronousinvocations, it needs to be a snapshot * captured at the point ofinvocation, to correctly satisfy the spec requirement. * @param receiver Receives the {@link ResponseContext}. Since the spec requires * that the asynchronous invocationsmust not update response context, * depending on the mode of invocationthey have to go to different places. * So we take a setter that abstractsthat away. */ protected final Packet process(Packet packet, RequestContext requestContext, ResponseContextReceiver receiver) { configureRequestPacket(packet, requestContext); Pool<Tube> pool = tubes; if (pool == null) throw new WebServiceException("close method has already been invoked"); // TODO: 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 be invoked 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);
try { while(!isBlocking() && !needsToReenter) { try { NextAction na; Tube last; if(throwable!=null) { if(contsSize==0) { // nothing else to execute. we are done. return null; } last = popCont(); if(traceEnabled) LOGGER.finer(getName()+' '+last+".processException("+throwable+')'); na = last.processException(throwable); } else { if(next!=null) { if(traceEnabled) LOGGER.finer(getName()+' '+next+".processRequest("+packet+')'); na = next.processRequest(packet); last = next; } else { if(contsSize==0) { // nothing else to execute. we are done. return null; } last = popCont(); if(traceEnabled) LOGGER.finer(getName()+' '+last+".processResponse("+packet+')'); 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) { throw new WebServiceException(e); } XMLStreamWriterFactory.recycle(writer); } return getContentType(packet.soapAction); } |
大家可以看到,他发出请求了。
1. 首先,服务器端有一个名叫JAXWSServlet的Servlet常驻服务器,监听请求。所以,请求会首先被转发给com.sun.enterprise.webservice.JAXWSServlet的
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException ,IOException{ /** * This requirement came from the jbi team. If theWebServiceEndpoint * is a jbi endpoint which is private throw an errorwhenever a get * or a post request is made */ Endpoint endpt = wsEngine_.getEndpoint(request.getServletPath()); ~省略~ |
2.最后,代码会调转到以下com.sun.xml.ws.transport.http.HttpAdapter的
final class HttpToolkit extends Adapter.Toolkit { public void handle(WSHTTPConnection con) throws IOException { boolean invoke = false; try { Packet packet = new Packet(); try { packet = decodePacket(con, codec); invoke = true; } catch(ExceptionHasMessage e) { LOGGER.log(Level.SEVERE, "JAXWS2015: An ExceptionHasMessage occurred. " + e.getMessage(), e); packet.setMessage(e.getFaultMessage()); } catch(UnsupportedMediaException e) { LOGGER.log(Level.SEVERE, "JAXWS2016: An UnsupportedMediaException occurred. " + e.getMessage(), e); con.setStatus(WSHTTPConnection.UNSUPPORTED_MEDIA); } catch(Exception e) { LOGGER.log(Level.SEVERE, "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) { LOGGER.log(Level.SEVERE, "JAXWS2018: An Exception occurred. " + e.getMessage(), e); if (!con.isClosed()) { writeInternalServerError(con); } return; } } encodePacket(packet, con, codec); } finally { if (!con.isClosed()) { con.close(); } } } } |
以上代码分为三块大的处理,分别是
packet = decodePacket(con, codec);
packet = head.process(packet, con.getWebServiceContextDelegate(),
packet.transportBackChannel);
encodePacket(packet, con, codec);
用来解析数据包,调用服务,发送回复数据包。这里就不详细介绍了。
希望能够通过这样的文章,更清晰的,直白的把WebService的详细技术内幕公布给大家,为开源软件运动作一份自己的贡献。