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服务器,具体安装方法可以参见以下文章:
2.2 下载WebService API(JAX-WS)的源代码
具体的下载URL参见以下地址:
2.3 部署服务器端
1.把1.3中的服务器端部署到GlassFish中
2.用wsimport命令生成stub中间程序
3.把1.3中的客户端代码和stub代码,以及2.2下载的WebService源代码部署到Eclipse工程中,就可以DEBUG了。
具体的可以参照以下文章:
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 @R_435_4045@ion
// 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的详细技术内幕公布给大家,为开源软件运动作一份自己的贡献。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。