0

我正在使用自上而下的方法开发 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。

4

2 回答 2

2

根据 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您的肥皂服务实现在哪里,并且EmptyServletContext只是一个完全空的实现,ServletContext它只为所有方法返回 null 或默认值。

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

于 2021-01-14T16:10:39.160 回答
1

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

基本上,我通过模拟或重新实现 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。

于 2012-08-30T01:06:06.150 回答