Skip to main content

Accessing OUT parameters in JAX-WS is rocket science?!?

10 replies [Last post]
ypomonh2
Offline
Joined: 2007-07-17

When I have a @WebParam with mode = Mode.OUT as a Holder like:

@WebParam(name = "Status", mode = Mode.OUT, partName = "Status")
Holder status

How can I access it from my client???

I have a small JAX-WS (2.1) client generated from NB 5.5 using an online WSDL.

My request is sent ok:

...

0
1234567

...

and the response I get look fine:

...

1

john

...

But when I try to access the returned values through the Holders:

try {
jaxwstest.XPortal_0020Interface service = new jaxwstest.XPortal_0020Interface();
jaxwstest.XPortal_0020InterfacePortType port = service.getXPortal_0020InterfacePort();
BigInteger bi = new BigInteger("0", 10);
java.math.BigInteger subsystemID = bi;
java.lang.String userNumber = "1234567";
javax.xml.ws.Holder status = null;
javax.xml.ws.Holder errorMessage = null;
javax.xml.ws.Holder userName = null;
port.existsUser(subsystemID, userNumber, status, errorMessage, userName);
System.out.println(status.value);
} catch (Exception ex) {
ex.printStackTrace();
}

status is still null :-(

(btw I use JAX-WS 2.1)

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
uprooter
Offline
Joined: 2008-03-05

Hi.
Im stuck with this issue and spent few days on it.
Can you please tell me if you have found any working example that using the Holder concept ?
Or anything else that can get me out of this hassle.

This is a rocket science !!

jitu
Offline
Joined: 2003-06-14

Holder should just work fine. There are quite a few samples that come with JAX-WS RI distribution. for e.g: mtom-large

kohlert
Offline
Joined: 2003-06-16

You need to initialize the Holders instead of assigning null to them, something like:

javax.xml.ws.Holder status = new javax.xml.ws.Holder();

ypomonh2
Offline
Joined: 2007-07-17

I tried:

Holder status = new Holder();

but got null and:

BigInteger bi = new BigInteger("0", 10); // = zero
Holder status = new Holder(bi);

but then:

port.existsUser(subsystemID, userNumber, status, errorMessage, userName);
System.out.println(status.value);

prints "0" which means that "status" hasn't changed after the WS has been called (?)

vivekp
Offline
Joined: 2003-06-10

I just replied to your posting on TSS, which is similar to what doug suggested. It should have worked, can you provide your WSDL contract for this operation?

-vivek.

ypomonh2
Offline
Joined: 2007-07-17

[b](vivek I appreciate the help!)

I start off using the following WSDL:[/b]

[i][b]--------- WSDL ---------[/b][/i]






























ExistsUser will return Status=0 if user does not exist, Status=1 if user exists and set UserName
accordingly, Status=-1 and set ErrorMeessage if an error occurs. It should be used to check if
user exists before creating username. UserNumber is the number the user has in the subsystem.
SubsystemID is the numerical ID of the subsystem
UserNumber is the number the user has in the subsystem.
SubsystemID is the numerical ID of the subsystem. UserType is the same value as
edupersonaffiliation
ChangePassword might return false if the user password is not strong enough. HTTP Authentication should be done
using the user username and password



[b]and the NB 5.5 Wizard that builds the following 2 files:

[i]--------- MyPortal_0020Interface.java ---------[/i][/b]
package jaxwsMyPortal;

import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;

@WebServiceClient(name = "MyPortal Interface", targetNamespace = "http://demo.test.myserver.com/ws/MyPortalinterface.php", wsdlLocation = "http://demo.test.myserver.com/ws/MyPortalinterface.php?wsdl")
public class MyPortal_0020Interface
extends Service
{
private final static URL MyPortal_0020INTERFACE_WSDL_LOCATION;

static {
URL url = null;
try {
url = new URL("http://demo.test.myserver.com/ws/MyPortalinterface.php?wsdl");
} catch (MalformedURLException e) {
e.printStackTrace();
}
MyPortal_0020INTERFACE_WSDL_LOCATION = url;
}
public MyPortal_0020Interface(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
public MyPortal_0020Interface() {
super(MyPortal_0020INTERFACE_WSDL_LOCATION, new QName("http://demo.test.myserver.com/ws/MyPortalinterface.php", "MyPortal Interface"));
}
@WebEndpoint(name = "MyPortal InterfacePort")
public MyPortal_0020InterfacePortType getMyPortal_0020InterfacePort() {
return (MyPortal_0020InterfacePortType)super.getPort(new QName("http://demo.test.myserver.com/ws/MyPortalinterface.php", "MyPortal InterfacePort"), MyPortal_0020InterfacePortType.class);
}
}

[i][b]--------- MyPortal_0020InterfacePortType.java ---------[/b][/i]
package jaxwsMyPortal;

import java.math.BigInteger;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebParam.Mode;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
import javax.xml.ws.Holder;

@WebService(name = "MyPortal InterfacePortType", targetNamespace = "http://demo.test.myserver.com/ws/MyPortalinterface.php")
@SOAPBinding(style = Style.RPC)
public interface MyPortal_0020InterfacePortType {

/**
* ExistsUser will return Status=0 if user does not exist, Status=1 if user exists and set UserName
* accordingly, Status=-1 and set ErrorMeessage if an error occurs. It should be used to check if
* user exists before creating username. UserNumber is the number the user has in the subsystem.
* SubsystemID is the numerical ID of the subsystem
*
* @param errorMessage
* @param status
* @param userName
* @param subsystemID
* @param userNumber
*/
@WebMethod(operationName = "ExistsUser", action = "http://demo.test.myserver.com/ws/MyPortalinterface.php/ExistsUser")
public void existsUser(
@WebParam(name = "SubsystemID", partName = "SubsystemID")
BigInteger subsystemID,
@WebParam(name = "UserNumber", partName = "UserNumber")
String userNumber,
@WebParam(name = "Status", mode = Mode.INOUT, partName = "Status")
Holder status,
@WebParam(name = "ErrorMessage", mode = Mode.OUT, partName = "ErrorMessage")
Holder errorMessage,
@WebParam(name = "UserName", mode = Mode.OUT, partName = "UserName")
Holder userName);

/**
* UserNumber is the number the user has in the subsystem.
* SubsystemID is the numerical ID of the subsystem. UserType is the same value as
* edupersonaffiliation
*
* @param errorMessage
* @param status
* @param userPassword
* @param userName
* @param subsystemID
* @param userNumber
* @param userType
*/
@WebMethod(operationName = "CreateUserName", action = "http://demo.test.myserver.com/ws/MyPortalinterface.php/CreateUserName")
public void createUserName(
@WebParam(name = "SubsystemID", partName = "SubsystemID")
BigInteger subsystemID,
@WebParam(name = "UserNumber", partName = "UserNumber")
String userNumber,
@WebParam(name = "UserName", partName = "UserName")
String userName,
@WebParam(name = "UserPassword", partName = "UserPassword")
String userPassword,
@WebParam(name = "UserType", partName = "UserType")
String userType,
@WebParam(name = "Status", mode = Mode.OUT, partName = "Status")
Holder status,
@WebParam(name = "ErrorMessage", mode = Mode.OUT, partName = "ErrorMessage")
Holder errorMessage);

/**
*
* @param errorMessage
* @param status
* @param userAlternateEmail
* @param userName
* @param userEmail
*/
@WebMethod(operationName = "ActivateServiceEmail", action = "http://demo.test.myserver.com/ws/MyPortalinterface.php/ActivateServiceEmail")
public void activateServiceEmail(
@WebParam(name = "UserName", partName = "UserName")
String userName,
@WebParam(name = "UserEmail", partName = "UserEmail")
String userEmail,
@WebParam(name = "UserAlternateEmail", partName = "UserAlternateEmail")
String userAlternateEmail,
@WebParam(name = "Status", mode = Mode.OUT, partName = "Status")
Holder status,
@WebParam(name = "ErrorMessage", mode = Mode.OUT, partName = "ErrorMessage")
Holder errorMessage);

/**
*
* @param errorMessage
* @param status
* @param userName
*/
@WebMethod(operationName = "ActivateServiceDialup", action = "http://demo.test.myserver.com/ws/MyPortalinterface.php/ActivateServiceDialup")
public void activateServiceDialup(
@WebParam(name = "UserName", partName = "UserName")
String userName,
@WebParam(name = "Status", mode = Mode.OUT, partName = "Status")
Holder status,
@WebParam(name = "ErrorMessage", mode = Mode.OUT, partName = "ErrorMessage")
Holder errorMessage);

/**
*
* @param errorMessage
* @param status
* @param userName
*/
@WebMethod(operationName = "ActivateServiceUserPage", action = "http://demo.test.myserver.com/ws/MyPortalinterface.php/ActivateServiceUserPage")
public void activateServiceUserPage(
@WebParam(name = "UserName", partName = "UserName")
String userName,
@WebParam(name = "Status", mode = Mode.OUT, partName = "Status")
Holder status,
@WebParam(name = "ErrorMessage", mode = Mode.OUT, partName = "ErrorMessage")
Holder errorMessage);

/**
* ChangePassword might return false if the user password is not strong enough. HTTP Authentication should be done
* using the user username and password
*
* @param errorMessage
* @param status
* @param userPassword
* @param userName
*/
@WebMethod(operationName = "ChangePassword", action = "http://demo.test.myserver.com/ws/MyPortalinterface.php/ChangePassword")
public void changePassword(
@WebParam(name = "UserName", partName = "UserName")
String userName,
@WebParam(name = "UserPassword", partName = "UserPassword")
String userPassword,
@WebParam(name = "Status", mode = Mode.OUT, partName = "Status")
Holder status,
@WebParam(name = "ErrorMessage", mode = Mode.OUT, partName = "ErrorMessage")
Holder errorMessage);

}

[b][i]--------- My client ---------[/i][/b]

package jaxwsMyPortal;

import java.math.BigInteger;
import javax.xml.ws.Holder;

public class Main {

/** Creates a new instance of Main */
public Main() {
}

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
try { // Call Web Service Operation
jaxwsMyPortal.MyPortal_0020Interface service = new jaxwsMyPortal.MyPortal_0020Interface();
jaxwsMyPortal.MyPortal_0020InterfacePortType port = service.getMyPortal_0020InterfacePort();
// TODO initialize WS operation arguments here
BigInteger bi = new BigInteger("0", 10);
java.math.BigInteger subsystemID = bi;
//subsystemID = 0;
java.lang.String userNumber = "1234567";
//javax.xml.ws.Holder status = new Holder();
Holder status = new Holder(bi);
//status = Holder(BigInteger bi);

javax.xml.ws.Holder errorMessage = null;
javax.xml.ws.Holder userName = null;
port.existsUser(subsystemID, userNumber, status, errorMessage, userName);
System.out.println("status.value = " + status.value);
//System.out.println(dd3f3);
} catch (Exception ex) {
System.out.println("------ E S K A S E ------");
ex.printStackTrace();
//System.out.println(System.getProperty("java.endorsed.dirs"));
}
// TODO code application logic here
}

}

[b]and the full communication between my client and the service:

[i]--------- TCP Stream ---------[/i][/b]
GET /ws/myportalinterface.php?wsdl HTTP/1.1
User-Agent: Java/1.6.0_01
Host: demo.test.myserver.com
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive

HTTP/1.1 200 OK
Date: Wed, 18 Jul 2007 09:44:54 GMT
Server: Apache/1.3.37 (Unix) PHP/4.4.7 with Suhosin-Patch mod_ssl/2.8.28 OpenSSL/0.9.7e
X-Powered-By: PHP/4.4.7
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/xml; charset=ISO-8859-1

1aed


>



























ExistsUser will return Status=0 if user does not exist, Status=1 if user exists and set UserName
.accordingly, Status=-1 and set ErrorMeessage if an error occurs. It should be used to check if
.user exists before creating username. UserNumber is the number the user has in the subsystem.
.SubsystemID is the numerical ID of the subsystem
UserNumber is the number the user has in the subsystem.
.SubsystemID is the numerical ID of the subsystem. UserType is the same value as
.edupersonaffiliation
ChangePassword might return false if the user password is not strong enough. HTTP Authentication should be done
.using the user username and password




0

POST /ws/myportalinterface.php HTTP/1.1
SOAPAction: "http://demo.test.myserver.com/ws/myportalinterface.php/ExistsUser"
Accept: text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Content-Type: text/xml; charset=utf-8
User-Agent: Java/1.6.0_01
Host: demo.test.myserver.com
Connection: keep-alive
Content-Length: 292




0
1234567
0



HTTP/1.1 200 OK
Date: Wed, 18 Jul 2007 09:44:56 GMT
Server: Apache/1.3.37 (Unix) PHP/4.4.7 with Suhosin-Patch mod_ssl/2.8.28 OpenSSL/0.9.7e
X-Powered-By: PHP/4.4.7
X-SOAP-Server: NuSOAP/0.7.2 (1.94)
Content-Length: 568
Keep-Alive: timeout=15, max=99
Connection: Keep-Alive
Content-Type: text/xml; charset=UTF-8




xmlns="http://demo.test.myserver.com/ws/myportalinterface.php">
1

john


jitu
Offline
Joined: 2003-06-14

Can you try with 2.1.1 or 2.1.2 and see if the problem still exists. Otherwise, file a bug. NB 5.5.1 has support for JAX-WS 2.1.1.

ypomonh2
Offline
Joined: 2007-07-17

(thanks for the suggestion jitu)

Just tried with 2.1.2 M1 and got the exact same behaviour. Will look into it a bit more and file a bug report if I don't get lucky.

Two quick questions:

1.) Is there anywhere working (!) code that uses the Holder concept? I couldn't find anything on the web, nor the samples.

2.) In the only book I could find jax-ws mentioned, "SOA Using Java Web Serices" the author states:

"[i](Holder for Out and In/Out Parameters) mechanism works, but I think it has dubious value and can even be dangerous. Suppose that you have other objects referencing your PurchaseOrder instance. These object references are not updated as a result of the Web service invocation—only the Holder class instance gets updated. Therefore, these references are now invalid and you will have to manually update them. In my opinion, the Holder concept (along with in/out and out parameters other than a return value) is misguided. You can’t pass an object reference to a Web service, and trying to make it seem as though you are leads to confusing and error-prone code."[/i]

Does this have to do with my case? What does it mean to "manually update" the object references?

jitu
Offline
Joined: 2003-06-14

I updated the bug. This is not related to Holder. For rpc/lit operations, the parnames should be in no namespace according BP. See
http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html

"4.7.20 Part Accessors

For rpc-literal envelopes, WSDL 1.1 is not clear what namespace, if any, the
accessor elements for parameters and return value are a part of. Different
implementations make different choices, leading to interoperability problems.

R2735 An ENVELOPE described with an rpc-literal binding MUST place the part
accessor elements for parameters and return value in no namespace."

xmlns="http://demo.test.myserver.com/ws/myportalinterface.php">
1

john

Here "Status" is in "http://demo.test.myserver.com/ws/myportalinterface.php"
namespace instead of no namespace. See how JAX-WS sends the requests with part
accessors in NO namespace. Change your PHP server accordingly.

But I think JAX-WS RI needs to flag a proper error message or accept the part accessors that are in some namespace. I think there are many implementations that do not conform to ws-i recommendations. I will file an enhancement for this.

jitu
Offline
Joined: 2003-06-14