zoukankan      html  css  js  c++  java
  • WebService服务细节

    Web服务部署内幕

    (作者:simbasun 转载请注明出处)

    1.第一口就吃到馅,配置一个Web服务的最简方法

    ===============================================================================================

    为了能让web服务先跑起来,先给出一个Web服务的原型,以便于后面的讨论。

    我们从一个最简单的例子开始,只给出必须的东西。

    所需软件:

    1.Tomcat4.1.2

    2.一个Java编译器,jdk或JBuilder等等,这是为了编译我们的Java源程序,于web服务无关。

    所需文件:

    1.sayHello.java

    2.web.xml

    3.server-config.xml

    4.Java Packages: axis.jar,jaxrpc.jar,tt-bytecode.jar,wsdl4j.jar,xercesImpl.jar,xml-apis.jar

    至于Tomcat怎么安装我就不说了,网上关于Tomcat安装的文章有很多。

    这六个package,从ibm和apache的网站上都可以下得到。

    只需要这些,我们就可以部署自己的Web服务了。。

    下面是目录结构:

    webapps/test/WEB-INF/web.xml

    webapps/test/WEB-INF/server-config.wsdd

    webapps/test/WEB-INF/classes/sayHello.class

    webapps/test/WEB-INF/lib/xxx.jar ---所需得六个packages

    web.xml

    ---------------------------------------------

    <?xml version="1.0" encoding="ISO-8859-1"?>;

    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">;

    <web-app>;

    <servlet>;

        <servlet-name>;Axis</servlet-name>;

        <!--实际servlet程序,这里是AxisServlet-->;

        <servlet-class>;org.apache.axis.transport.http.AxisServlet</servlet-class>;

    </servlet>;

    <!-- ### 定义servlet和url的对应关系-->;

    <servlet-mapping>;

        <servlet-name>;Axis</servlet-name>;

        <url-pattern>;/services/*</url-pattern>;

    </servlet-mapping>;

    </web-app>;

    ---------------------------------------------

    server-config.wsdd

    ---------------------------------------------

    <?xml version="1.0" encoding="UTF-8"?>;

    <deployment xmlns:java="http://xml.apache.org/axis/wsdd/providers/java" xmlns="http://xml.apache.org/axis/wsdd/">;

    <handler type="java:org.apache.axis.handlers.http.URLMapper" name="URLMapper"/>;

    <service name="sayHelloService" provider="java:RPC">;

      <parameter name="className" value="sayHello"/>;

      <parameter name="allowedMethods" value="sayHelloTo"/>;

    </service>;

    <transport name="http">;

      <requestFlow>;

        <handler type="URLMapper"/>;

      </requestFlow>;

    </transport>;

    </deployment>;

    ---------------------------------------------

    sayHello.java

    ---------------------------------------------

    public class sayHello

    {

      public String sayHelloTo(String aname)

      {

        return "How are you, " + aname;

      }

    }

    ---------------------------------------------

    假设ip地址192.168.0.1,端口号是80,我们输入下面的url得到服务列表(当然这里只有一个):

    http://192.168.0.1/test/services

    如果你的端口号是8080,就应该输入http://192.168.0.1:8080/test/services,后面同理。

    浏览器显示:

    ——————————————

    |And now... Some Services |

    | sayHelloService (wsdl)  |

    |  .sayHelloTo            |   

    ——————————————

    sayHelloService是我们的服务名,右侧的 (wsdl)是一个链接指向sayHelloService的WSDL文档,

    这个文档是由Axis自动生成的。

    sayHelloTo当然就是我们的方法了。。。

    点击(wsdl)链接或输入下面的url,得到WSDL:

    http://192.168.0.1/test/services/sayHelloService?wsdl

    浏览器显示sayHelloService的WSDL文档:

    <?xml version="1.0" encoding="UTF-8"?>;

    <wsdl:definitions targetNamespace="http://192.168.0.1/test/services/sayHelloService/test/services/sayHelloService" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="http://192.168.0.1/test/services/sayHelloService/test/services/sayHelloService-impl" xmlns:intf="http://192.168.0.1/test/services/sayHelloService/test/services/sayHelloService" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">;

      <wsdl:message name="sayHelloToResponse">;

        <wsdl:part name="return" type="xsd:string"/>;

      </wsdl:message>;

      <wsdl:message name="sayHelloToRequest">;

        <wsdl:part name="aname" type="xsd:string"/>;

      </wsdl:message>;

      <wsdl:portType name="sayHello">;

        <wsdl:operation name="sayHelloTo" parameterOrder="aname">;

          <wsdl:input message="intf:sayHelloToRequest" name="sayHelloToRequest"/>;

          <wsdl:output message="intf:sayHelloToResponse" name="sayHelloToResponse"/>;

        </wsdl:operation>;

      </wsdl:portType>;

      <wsdl:binding name="sayHelloServiceSoapBinding" type="intf:sayHello">;

        <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>;

        <wsdl:operation name="sayHelloTo">;

          <wsdlsoap:operation soapAction=""/>;

          <wsdl:input name="sayHelloToRequest">;

            <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://192.168.0.1/test/services/sayHelloService/test/services/sayHelloService" use="encoded"/>;

          </wsdl:input>;

          <wsdl:output name="sayHelloToResponse">;

            <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://192.168.0.1/test/services/sayHelloService/test/services/sayHelloService" use="encoded"/>;

          </wsdl:output>;

        </wsdl:operation>;

      </wsdl:binding>;

      <wsdl:service name="sayHelloService">;

        <wsdl:port binding="intf:sayHelloServiceSoapBinding" name="sayHelloService">;

          <wsdlsoap:address location="http://192.168.0.1/test/services/sayHelloService"/>;

        </wsdl:port>;

      </wsdl:service>;

    </wsdl:definitions>;

    我们甚至不用客户端,就可以查看服务是否部署成功以及获得返回结果

    用Get方法获得soap流,我们要用下面的url:

    (真正调用Web服务,用的是Post方法,这个后面会讲)

    http://192.168.0.1/test/services/sayHelloService?method=sayHelloTo&aname=everybody

    浏览器显示的是乱码,我们点右键查看源文件,结果如下:

    <p>;Got response message</p>;

    <?xml version="1.0" encoding="UTF-8"?>;

    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">;

     <soapenv:Body>;

      <sayHelloToResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">;

       <sayHelloToReturn xsi:type="xsd:string">;How are you, everybody</sayHelloToReturn>;

      </sayHelloToResponse>;

     </soapenv:Body>;

    </soapenv:Envelope>;

    这就是我们想要的结果吗?这只是服务器端送回来的SOAP消息,不过我们想要的结果在里面。。。

    为了真正调用我们的Web服务,下面给出一个Client:

    import org.apache.axis.client.Call;

    import org.apache.axis.client.Service;

    import javax.xml.namespace.QName;

      public class test

      {

        public static void main(String [] args)

        {

          try {

                 String endpoint = "http://192.168.0.1/test/services/sayHelloService";

                 Service  service = new Service();

                 Call     call    = (Call) service.createCall();

                 call.setTargetEndpointAddress( new java.net.URL(endpoint) );

                 call.setOperationName(new QName("http://sayHelloService", "sayHelloTo"));

                 String ret = (String) call.invoke( new Object[] { args[0] } );

                 System.out.println(ret);

             } catch (Exception e) {

                 e.printStackTrace();

             }

        }

      }

      注意要配置好正确的classpath,确保编译器能找的到axis.jar和jaxrpc.jar,否则编译不会通过。

      用下面的命令行运行这个class:

      java test everybody

      我们会得到:How are you, everybody

      这才是我们真正想要的。。。

    2.追根究底,我们的Web服务是怎样跑起来的

    ===============================================================================================

    前面给出的两个配置文件web.xml和server-config.wsdd,或许不是能一下子就看懂的。

    先让我们回顾一下servlet的映射模式。

    我们知道,servlet是从javax.servlet.http.HttpServlet继承的,在服务器端被载入JVM执行,然后向客户端输出html流。

    servlet的web.xml文件(位于 webapps/foo/WEB-INF目录):

    <?xml version="1.0" encoding="UTF-8"?>;

    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"

                             "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">;

    <web-app>;

    <servlet-mapping>;

    <servlet-name>;invoker</servlet-name>;

    <url-pattern>;/servlet/*</url-pattern>;

    </servlet-mapping>;

    </web-app>;

    invoker servlet 其实是:org.apache.catalina.servlets.InvokerServlet

    按类名提供小服务程序。例如,如果您调用 foo/servlet/HelloServlet,

    invoker servlet将装入该HelloServlet(如果它在其类路径中的话)并执行。

    初看上面的web.xml,好像只给出了一个servlet映射,而没有定义invoker servlet。

    其实,invoker servlet 是在tomcat的conf目录中的web.xml中定义的::

     <servlet>;

            <servlet-name>;invoker</servlet-name>;

            <servlet-class>;

              org.apache.catalina.servlets.InvokerServlet

            </servlet-class>;

            <init-param>;

                <param-name>;debug</param-name>;

                <param-value>;0</param-value>;

            </init-param>;

            <load-on-startup>;2</load-on-startup>;

        </servlet>;

    所以,如果抛开Tomcat_HOME/conf/web.xml,我们这样定义一个web.xml,似乎更能清楚的说明问题:

    <?xml version="1.0" encoding="UTF-8"?>;

    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"

                             "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">;

    <web-app>;

    <servlet-name>;MyInvoker</servlet-name>;

    <servlet-class>;

    org.apache.catalina.servlets.InvokerServlet

    </servlet-class>;

    <init-param>;

    <param-name>;debug</param-name>;

    <param-value>;0</param-value>;

    </init-param>;

    <load-on-startup>;2</load-on-startup>;

    </servlet>;

    <servlet-mapping>;

    <servlet-name>;MyInvoker</servlet-name>;

    <url-pattern>;/servlet/*</url-pattern>;

    </servlet-mapping>;

    </web-app>;

    即所有/servlet/* 模式的url,都会交给org.apache.catalina.servlets.InvokerServlet来处理。

    或者说,所有/servlet/* 模式的url,其实都是调用InvokerServlet这个类,而InvokerServlet本身也是

    一个servlet,它也是从 HttpServlet 继承而来的。

    这样,我们自己的servlet就能够通过特定的url执行,即 /servlet/OurServlet。

    当然,如果你高兴,可以定义任何的 url pattern,而不一定是 /servlet/*,这一点,正如我们后面

    看到的Axis处理Soap消息的方法。

    再进一步,如果不想让 InvokerServlet 在中间“捣鬼”,我们当然可以直接定义自己的servlet:

    <?xml version="1.0" encoding="UTF-8"?>;

    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"

                             "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">;

    <web-app>;

    <servlet-name>;MyInvoker2</servlet-name>;

    <servlet-class>;

    com.foo.MyServlet

    </servlet-class>;

    </servlet>;

    <servlet-mapping>;

    <servlet-name>;MyInvoker2</servlet-name>;

    <url-pattern>;/AnyName/*</url-pattern>;

    </servlet-mapping>;

    </web-app>;

    JSP也是一样的道理,有了上面的分析,

    看看Tomcat_HOME/conf/web.xml中的如下语句就可以JSP的处理方法了,这里就不再废话了:

    ....

    <servlet>;

            <servlet-name>;jsp</servlet-name>;

            <servlet-class>;org.apache.jasper.servlet.JspServlet</servlet-class>;

            <init-param>;

                <param-name>;logVerbosityLevel</param-name>;

                <param-value>;WARNING</param-value>;

            </init-param>;

            <load-on-startup>;3</load-on-startup>;

    </servlet>;

    <servlet-mapping>;

        <servlet-name>;jsp</servlet-name>;

        <url-pattern>;*.jsp</url-pattern>;

    </servlet-mapping>;

    ....

    下面进入正题。

    我们先来看部署Web Service的web.xml:

    <?xml version="1.0" encoding="ISO-8859-1"?>;

    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">;

    <web-app>;

    <servlet>;

        <servlet-name>;Axis</servlet-name>;

        <!--实际servlet程序,这里是AxisServlet-->;

        <servlet-class>;org.apache.axis.transport.http.AxisServlet</servlet-class>;

    </servlet>;

    <!-- ### 定义servlet和url的对应关系-->;

    <servlet-mapping>;

        <servlet-name>;Axis</servlet-name>;

        <url-pattern>;/services/*</url-pattern>;

    </servlet-mapping>;

    </web-app>;

    所有 /services/* 模式的 url 都会交给org.apache.axis.transport.http.AxisServlet处理,

    AxisServlet当然也是从HttpServlet继承而来的。这就是为什么我们部署的Web服务在调用时都要在

    服务名称前加上 services/ 了。

    可以说,AxisServlet是所有Web服务调用的入口。

    那么AxisServlet在接手Web服务调用后都做了哪些工作呢?

    客户端用call.invoke()调用web服务用的是POST,所以入口是AxisServlet.doPost...

    而不是AxisServlet.doGet...

    先来看看AxisServlet的doPost函数,这里只给出了关键语句及注释:

        /**

         * Process a POST to the servlet by handing it off to the Axis Engine.

         * Here is where SOAP messages are received

         * @param req posted request

         * @param res respose

         * @throws ServletException trouble

         * @throws IOException different trouble

         */

         public void doPost(HttpServletRequest req, HttpServletResponse res)

            throws ServletException, IOException

        {

    msgContext = createMessageContext(engine, req, res);//获取客户请求信息

    engine.invoke(msgContext); //调用客户端请求的服务

    responseMsg = msgContext.getResponseMessage();//得到调用的返回结果

    sendResponse(getProtocolVersion(req), contentType, res, responseMsg);//将结果送至客户端

         }

    这样一来,Web服务调用的来龙去脉就大致清楚了。。。

    为了高清楚前面我们的三个url

    http://192.168.0.1/test/services

    http://192.168.0.1/test/services/sayHelloService?wsdl

    http://192.168.0.1/test/services/sayHelloService?method=sayHelloTo&aname=everybody

    是怎样获得输出结果的,再来看看AxisServlet的doGet函数,这里只给出了流程框架及注释:

    **

    * Process GET requests. Because Axis does not support the GET-style

    * pseudo execution of SOAP methods, this handler deals with queries

    * of various kinds, not real SOAP actions.

    *

    * @todo for secure installations, dont stack trace on faults

    * @param request request in

    * @param response request out

    * @throws ServletException

    * @throws IOException

    */

    public void doGet(HttpServletRequest req, HttpServletResponse res)

            throws ServletException, IOException

    {

    //如果路径为空,比如:http://localhost/wstk/services 或 http://localhost/wstk/services/*

    if((pathInfo == null || pathInfo.equals("")) && !realpath.endsWith(".jws"))

    {

    //从server-config.wsdd文件中读取所有部署的服务信息,向向客户端列出所有部署的服务,

    //包括每个服务可调用的方法。

    }else

    //如果路径不为空,比如:http://localhost/wstk/services/sayHelloService

    if(realpath != null)

    {

    //如果请求wsdl,比如:http://localhost/wstk/services/sayHelloService?wsdl

    if(wsdlRequested)

    {

    //创建sayHelloService的WSDL文件并传送至客户端

    } else

    //这里是利用url调用Web服务的入口,比如http://192.168.0.1/test/services/sayHelloService?method=sayHelloTo&aname=everybody

    if(req.getParameterNames().hasMoreElements())

    {

    //如果客户端调用的方法正确,则Axis会调用相应的JavaBean,并把JavaBean的返回结果

    //封装为Soap消息流返回给客户端。

    }

    }

    }

    而Axis怎样找到我们所请求的JavaBean呢?答案是server-config.wsdd文件。

    server-config.wsdd

    <?xml version="1.0" encoding="UTF-8"?>;

    <deployment xmlns:java="http://xml.apache.org/axis/wsdd/providers/java" xmlns="http://xml.apache.org/axis/wsdd/">;

    <service name="sayHelloService" provider="java:RPC">;

      <parameter name="className" value="sayHello"/>;

      <parameter name="allowedMethods" value="sayHelloTo"/>;

    </service>;

    <handler type="java:org.apache.axis.handlers.http.URLMapper" name="URLMapper"/>;

    <transport name="http">;

      <requestFlow>;

        <handler type="URLMapper"/>;

      </requestFlow>;

    </transport>;

    </deployment>;

    WSDD是web service deployment descriptor的缩写。

    最外面的<deployment>;元素指示这是WSDD,并定义了java的名字空间。

    接着的 <service>;元素定义了service。一个service是一个目标链,包括请求request、内容提供者provider、响应response。

    在这个例子中,我们指出service名字是sayHelloService ,provider是"java:RPC",它是axis 的标记,指示这是一个java的RPC service,

    而处理它的真正的class是org.apache.axis.providers.java.RPCProvider。

    接着我们要在<parameter>;中告诉RPCProvider,它如何实例化并调用正确的class(如:com.foo.MyService)。

    <parameter>;元素的className指示class名,allowedMethods告诉引擎那些共用的方法要通过soap来调用。

    "*"表示所有的公共方法,我们也列出方法名字列表,可以空格或逗号分割它们。

  • 相关阅读:
    由二进制移位想到的
    KDJ指标详解
    PMP考试结束
    转K线理论初级二
    日本地震效应
    Baseline之流水先生的见解
    KDJ判断原则
    转K线理论初级一
    管理学法则
    今天提到KW,特此@Mark一下
  • 原文地址:https://www.cnblogs.com/leonbond/p/2373831.html
Copyright © 2011-2022 走看看