JAX-WS:有状态 WS 在独立进程中失败
我在 Tomcat 上部署了一个有状态的 Web 服务。它由工厂服务和主要 API 服务组成,并且工作得很好。工厂服务将 W3CEndpointReference 返回到主 API 实例,客户端使用会话。
现在,我尝试将相同的服务作为独立应用程序运行。在这种情况下,工厂返回的 W3CEndpointReference 突然开始指向工厂 URI,而不是主服务之一。
当比较 Tomcat 和独立运行的引用时,很明显独立引用具有错误的 URI。具体来说,它指向工厂 URI,而不是主 API。
这是正确的参考:
...
<ns3:Address>http://localhost:8080/td2ws/api</ns3:Address>
...
这是在独立进程上调用工厂时的参考:
<Address>http://localhost:9009/td2ws/factory</Address>
我的理解是,servlet 上下文下的某些代码了解服务类 (Td2Ws) 与其 URI 之间的对应关系,并相应地调整参考。不过,该代码在独立进程下不起作用。我什至怀疑代码使用了 sun-jaxws.xml,但我不知道如何“打开它”。
如何在独立应用程序中使有状态 Web 服务工作?
以下是代码的相关部分:
工厂服务(无状态):
@WebService(targetNamespace="http://server.td2ws.sf.net/")
public class Td2WsFactory {
@WebMethod(operationName="StartSession", action="urn:StartSession")
@WebResult(name="Reference")
public W3CEndpointReference startSession() {
StatefulWebServiceManager<Td2Ws> manager = Td2Ws.getManager();
// for standalone execution only
if( manager == null ) {
manager = new StatefulInstanceResolver<Td2Ws>(Td2Ws.class);
Td2Ws.setManager(manager);
}
Td2Ws session = new Td2Ws();
return manager.export(W3CEndpointReference.class,session);
}
}
有状态 API:
@Stateful
@WebService(targetNamespace="http://server.td2ws.sf.net/")
@Addressing
public class Td2Ws {
private static StatefulWebServiceManager<Td2Ws> manager;
public static void setManager(StatefulWebServiceManager<Td2Ws> manager ) {
Td2Ws.manager = manager;
}
public static StatefulWebServiceManager<Td2Ws> getManager() {
return Td2Ws.manager;
}
@WebMethod(operationName="Login", action="urn:Login")
public void login(
@WebParam(name="Username") String username,
@WebParam(name="Password") String password,
@WebParam(name="Domain") String domain,
@WebParam(name="Project") String project
) throws Td2WsException {
客户端代码(针对独立应用程序):
public static void main(String[] args) throws Exception {
Endpoint epf = Endpoint.publish("http://localhost:9009/td2ws/factory", new net.sf.td2ws.server.Td2WsFactory());
Endpoint eps = Endpoint.publish("http://localhost:9009/td2ws/api", new net.sf.td2ws.server.Td2Ws());
URL factoryUrl = new URL("http://localhost:9009/td2ws/factory?wsdl");
QName factoryQname = new QName("http://server.td2ws.sf.net/", "Td2WsFactoryService");
URL sessionUrl = new URL("http://localhost:9009/td2ws/api?wsdl");
QName sessionQname = new QName("http://server.td2ws.sf.net/", "Td2WsService");
Td2WsFactory factory = new Td2WsFactoryService(factoryUrl,factoryQname).getTd2WsFactoryPort();
System.out.println("factory: "+factory);
W3CEndpointReference session = factory.startSession();
System.out.println("session: "+session);
Td2WsService service = new Td2WsService(sessionUrl,sessionQname);
System.out.println("service: "+service);
Td2Ws port = service.getPort(session,Td2Ws.class);
System.out.println("port: "+port);
port.login(
Config.get("td.user"),
Config.get("td.pass"),
Config.get("td.domain"),
Config.get("td.project"));
System.out.println("logged in...");
该代码在登录时失败( ),因为目标对象没有登录操作(毕竟,它错误地定位到工厂 URI):
Exception in thread "main" javax.xml.ws.soap.SOAPFaultException:
Cannot find dispatch method for {http://server.td2ws.sf.net/}Login
I have a stateful web service deployed onto Tomcat. It consists of factory service and main API service, and works just fine. Factory service returns a W3CEndpointReference to main API instance, and client uses the session.
Now, I'm trying to run the very same service as a standalone application. In this case, the W3CEndpointReference returned by the factory suddenly starts to point to factory URI, not to main service one.
When references from runs against Tomcat and standalone are compared, it becomes clear that standalone reference has a wrong URI. Specifically, it points to factory URI, not main API one.
Here's correct reference:
...
<ns3:Address>http://localhost:8080/td2ws/api</ns3:Address>
...
Here's references when factory is called on standalone process:
<Address>http://localhost:9009/td2ws/factory</Address>
My understanding that some code under servlet context has a knowledge of correspondence between service class (Td2Ws) and it's URI, and adjusts the references accordingly. That piece of code is not in effect under standalone process though. I can even suspect that code uses sun-jaxws.xml, but I don't know how to "turn it on".
How can I make stateful web service in standalone application work?
Here are relevant parts of the code:
Factory service (stateless):
@WebService(targetNamespace="http://server.td2ws.sf.net/")
public class Td2WsFactory {
@WebMethod(operationName="StartSession", action="urn:StartSession")
@WebResult(name="Reference")
public W3CEndpointReference startSession() {
StatefulWebServiceManager<Td2Ws> manager = Td2Ws.getManager();
// for standalone execution only
if( manager == null ) {
manager = new StatefulInstanceResolver<Td2Ws>(Td2Ws.class);
Td2Ws.setManager(manager);
}
Td2Ws session = new Td2Ws();
return manager.export(W3CEndpointReference.class,session);
}
}
Stateful API:
@Stateful
@WebService(targetNamespace="http://server.td2ws.sf.net/")
@Addressing
public class Td2Ws {
private static StatefulWebServiceManager<Td2Ws> manager;
public static void setManager(StatefulWebServiceManager<Td2Ws> manager ) {
Td2Ws.manager = manager;
}
public static StatefulWebServiceManager<Td2Ws> getManager() {
return Td2Ws.manager;
}
@WebMethod(operationName="Login", action="urn:Login")
public void login(
@WebParam(name="Username") String username,
@WebParam(name="Password") String password,
@WebParam(name="Domain") String domain,
@WebParam(name="Project") String project
) throws Td2WsException {
Client code (targeted to standalone):
public static void main(String[] args) throws Exception {
Endpoint epf = Endpoint.publish("http://localhost:9009/td2ws/factory", new net.sf.td2ws.server.Td2WsFactory());
Endpoint eps = Endpoint.publish("http://localhost:9009/td2ws/api", new net.sf.td2ws.server.Td2Ws());
URL factoryUrl = new URL("http://localhost:9009/td2ws/factory?wsdl");
QName factoryQname = new QName("http://server.td2ws.sf.net/", "Td2WsFactoryService");
URL sessionUrl = new URL("http://localhost:9009/td2ws/api?wsdl");
QName sessionQname = new QName("http://server.td2ws.sf.net/", "Td2WsService");
Td2WsFactory factory = new Td2WsFactoryService(factoryUrl,factoryQname).getTd2WsFactoryPort();
System.out.println("factory: "+factory);
W3CEndpointReference session = factory.startSession();
System.out.println("session: "+session);
Td2WsService service = new Td2WsService(sessionUrl,sessionQname);
System.out.println("service: "+service);
Td2Ws port = service.getPort(session,Td2Ws.class);
System.out.println("port: "+port);
port.login(
Config.get("td.user"),
Config.get("td.pass"),
Config.get("td.domain"),
Config.get("td.project"));
System.out.println("logged in...");
That code fails on login() as the target object has no Login operation (it's wrongly targeted to factory URI, after all):
Exception in thread "main" javax.xml.ws.soap.SOAPFaultException:
Cannot find dispatch method for {http://server.td2ws.sf.net/}Login
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我已经有一段时间没有这样做了,但我看了一下我前一段时间构建的功能性有状态 Web 服务。我的注释与你的非常相似,除了我在班级顶部有这个:
注意没有修饰符,如果有其他的话,我认为它需要公开,但我并不肯定。容器如何知道如何设置这个让我感到困惑,但它确实有效。它必须只查找 WebServiceContext 类型的任何 @Resource 并设置它。您可以像这样访问会话信息:
I haven't done this in awhile, but I took a look at a functional stateful web service that I built some time ago. My Annotations are very similar to yours, except I have this at the top of my class:
Notice there is no modifier, if anything else I think it needs to be public, but I'm not positive. How the container knows to set this beats me, but it works. It must just look for any @Resource of type WebServiceContext and set it. You can access session information like so: