以编程方式获取JAX-WS WebService实现的Servlet实例?

时间:2012-08-24 04:32:03

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

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

2 个答案:

答案 0 :(得分: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 是您的soap 服务实现,而EmptyServletContext 只是一个完全空的ServletContext 实现,它只为所有方法返回null 或默认值。

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

答案 1 :(得分:1)

好的,经过大量的实验,我已经能够把一些有用的东西放在一起。为了文档起见,我会在这里发布。

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

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

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

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

最后,我创建了一个HttpServlet实现,它将请求转发给WSServletDelegate,类似于WSServlet的工作方式。由于WSServlet再次将委托对象存储在ServletContext init params中,因此我发现重新实现其功能比使用现有WSServlet更容易。然后,可以将HttpServlet的这种实现视为接受和处理SOAP Web服务调用的任何普通Servlet。