【问题标题】:Programmatically obtain a Servlet instance for a JAX-WS WebService implementation?以编程方式获取 JAX-WS WebService 实现的 Servlet 实例?
【发布时间】:2012-08-24 04:32:03
【问题描述】:

我正在使用自顶向下的方法开发 Web 服务,使用 JAX-WS 的 wsimport 从 WSDL 生成服务类型和接口。这提供了一个如下的端口类型接口,我实现了。

/**
 * This class was generated by the JAX-WS RI.
 * JAX-WS RI 2.2.4-b01
 * Generated source version: 2.2
 */
@WebService(name = "ExamplePortType", targetNamespace = "http://example.com")
public interface ExamplePortType {

    /**
     * @param example
     * @return java.lang.String
     * @throws ExampleException
     */
    @WebMethod
    @WebResult(name = "exampleResponse", targetNamespace = "http://example.com")
    @RequestWrapper(localName = "sendExample", targetNamespace = "http://example.com", className = "com.example.SendExample")
    @ResponseWrapper(localName = "sendExampleResponse", targetNamespace = "http://example.com", className = "com.example.SendExampleResponse")
    public String sendExample(
        @WebParam(name = "example", targetNamespace = "http://example.com")
        ExampleRequest example)
        throws ExampleException
    ;
}

似乎将此服务添加到您的应用程序服务器(在我的例子中是 Tomcat)的正常方法是将实现类作为 servlet 添加到 web.xml 并添加 WSServletContextListener 作为侦听器。粗略地说,似乎在初始化上下文时,侦听器构造了 ServletAdapters,它包装了实现 bean 并将它们添加到由瘦 WSServlet 调用的 WSServletDelegate。然后,对您的实现的请求由 WSServlet 处理,并由委托根据您设置的任何 URL 模式传递给您的 bean。

有没有办法以编程方式完成上述操作?我想要一个接受上述接口实例并返回给我一个 Servlet 实例的方法,如果在 ServletContext 中注册,它会将适当的请求路由到包装的实现。比如:

Servlet exampleServlet = new ServletAdapter().wrap(new ExamplePortTypeImpl());

一个要求是我不能依赖静态配置文件(例如 web.xml 或 sun-jaxws.xml)。 JAX-WS 或相关库(即:Axis2 等)是否提供这种功能?

如果有不清楚的地方,请道歉;这是我第一次来这里:)。任何指针表示赞赏。

目前使用 JAX-WS 2.1、Tomcat 7、Servlet 3.0。

【问题讨论】:

    标签: jakarta-ee jax-ws servlet-3.0 jax-ws-customization


    【解决方案1】:

    根据 Anthop 的回答,这是我想出的适合我的代码。

    // Main code:
    
    final ServletContextHandler soapContextHandler = createSoapServletContextHandler();
    
    final HandlerList handlers = new HandlerList();
    handlers.setHandlers(new Handler[] { soapContextHandler });
    
    server = createServer(port);
    server.setHandler(handlers);
    server.start();
    
    // Function:
        private ServletContextHandler createSoapServletContextHandler()
        {
            final ServletContextHandler soapContextHandler =
                    new ServletContextHandler(ServletContextHandler.SESSIONS);
    
            WSEndpoint<SoapServiceImpl> endpoint =
                    WSEndpoint.create(SoapServiceImpl.class, false, null, null, null, null,
                            BindingImpl.getDefaultBinding(), null, null, null, false);
    
            ServletAdapter servletAdapter = new ServletAdapterList().createAdapter("soap", "/soap", endpoint);
    
            SoapServlet soapServlet = new SoapServlet(
                    new WSServletDelegate(Collections.singletonList(servletAdapter), new EmptyServletContext()));
    
            soapContextHandler.addServlet(new ServletHolder(soapServlet), "/soap");
            return soapContextHandler;
        }
    
    // Servlet:
        private static class SoapServlet extends HttpServlet
        {
            private final WSServletDelegate delegate;
    
            SoapServlet(WSServletDelegate delegate)
            {
                this.delegate = delegate;
            }
    
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
            {
                delegate.doGet(req, resp, getServletContext());
            }
    
            @Override
            protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
            {
                delegate.doPost(req, resp, getServletContext());
            }
        }
    

    其中SoapServiceImpl 是您的soap 服务实现,而EmptyServletContext 只是ServletContext 的一个完全空的实现,它只返回null 或所有方法的默认值。

    这应该使用 Jetty 为路径 /soap 上的肥皂请求提供服务。您可以将其他上下文处理程序添加到列表中,它们也将与肥皂处理程序一起工作。我已经与 Jersey 上下文处理程序一起对其进行了测试,因此我的服务使用 Jetty 在同一端口上处理 REST 和 SOAP 请求。

    【讨论】:

      【解决方案2】:

      好的,经过大量试验,我已经能够组合出一些可行的东西。为了文档,我将其发布在这里。

      基本上,我通过模拟或重新实现 JAX-WS 如何从它的 WSServletContextListener 创建它的 WSServlet 来实现这一点。不幸的是,我找不到更简单或更好的支持和直接的方法来做到这一点。

      基本上,我的工厂使用它的 WSEndpoint.create() 方法创建一个 WSEndpoint。 create() 将我试图包装的 WebService 实现的类作为输入。大多数其他输入为空,以鼓励 WSEndpoint 使用默认值或尝试从实现类中的注释中提取一些配置。

      然后我使用一个新的 ServletAdapterList() 来创建一个使用先前创建的 WSEndpoint 的 ServletAdapter。为此,由于我为每个实现类创建一个 Servlet,因此适配器的名称无关紧要,并且可以将其配置为将所有流量发送到实现,而不管 URL 是什么。

      然后,我使用先前 ServletAdapter 的单例列表创建一个新的 WSServletDelegate()。这里唯一的技巧是 JAX-WS 似乎有一个坏习惯,至少对于这一步和下一步,在 ServletContext 的 init 参数中存储一些值。即使参数不在上下文中,它也没有正确地进行空检查,即使它有默认使用。所以我最后还创建了一个 ServletContext 的虚拟实现,我将它传递给 WSServletDelegate 构造函数,让它感觉像是在查找 init 参数。

      最后,我创建了一个 HttpServlet 实现,它将请求转发到 WSServletDelegate,类似于 WSServlet 的做法。由于 WSServlet 再次将委托对象存储在 ServletContext 初始化参数中,我发现重新实现它的功能比弄乱现有的 WSServlet 更容易。然后可以将 HttpServlet 的这种实现视为任何将接受和处理 SOAP Web 服务调用的普通 Servlet。

      【讨论】:

        猜你喜欢
        • 2016-04-19
        • 1970-01-01
        • 2011-03-16
        • 1970-01-01
        • 2011-08-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多