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