CF / Lucee incompatibility for createObject() / SOAP request

I’m migrating an application from CF 9.01 to Lucee 5.3.3.62, and having trouble with using a webservice (SOAP).

This is the sample code:

<cfscript>
	ws = createObject ("webservice", "http://10.0.0.100/project-manager-gui/services/v2/XTMCustomerMTOMWebService?wsdl");
	XTM_getXTMInfo = ws.getXTMInfo({password="*****", userId=99, client="*******"},{});

	XTM_getXTMInfo_xtmInfo_logo = XTM_getXTMInfo.xtmInfo.logo;
	XTM_getXTMInfo_xtmInfo_version = XTM_getXTMInfo.xtmInfo.version;
	XTM_getXTMInfo_xtmInfo_companyname = XTM_getXTMInfo.xtmInfo.companyname;
	XTM_getXTMInfo_xtmInfo_website = XTM_getXTMInfo.xtmInfo.website;
</cfscript>

<cfoutput>
	#XTM_getXTMInfo_xtmInfo_logo#
	<br>
	#XTM_getXTMInfo_xtmInfo_version#
	<br>
	#XTM_getXTMInfo_xtmInfo_companyname#
	<br>
	#XTM_getXTMInfo_xtmInfo_website#
	<br>
</cfoutput>

CF 9.01 Output:

https://xtm-intl.com/wp-content/themes/xtm/images/xtmintl-logo.png
11.0
XTM International
https://www.xtm-intl.com

Lucee 5.3.3.62:

Lucee 5.3.3.62 Error (java.lang.NoSuchMethodException)
Message puydf1hvwoi8.loginAPI.setUserid(java.lang.Long)
Stacktrace The Error Occurred in
C:\www\gunter\CF_XTM_Test1.cfm: line 3

1: <cfscript>
2: ws = createObject ("webservice", "http://10.0.0.100/project-manager-gui/services/v2/XTMCustomerMTOMWebService?wsdl");
3: XTM_getXTMInfo = ws.getXTMInfo({password="****", userId=99, client="******"},{});
4:
5: XTM_getXTMInfo_xtmInfo_logo = XTM_getXTMInfo.xtmInfo.logo;

It appears that setUserid() does not exist, but it does, obviously because it works in ACF, but also, when I try to feed it an invalid argument:

XTM_getXTMInfo = ws.getXTMInfo({password="****", userId="TEST", client="******"},{});

Lucee throws:

can’t cast [test] string to a number value

So, the setUserid() function is found, and is expecting a Long as userId argument.

Anybody an idea, or workaround? Thanks!

Gunter, I’m not so sure your conclusions are correct (though I understand how you would come to them). But better, I have a tool to propose that should help you (which we can’t use, since that wsdl url is not open to us publicly).

  • First, as for the error I don’t think it’s saying that setuserid does not exist, but rather just that a call to it with the given argument types used did not find a method with that type signature.

  • Second, you say it wants to use long, but instead I think it’s that Lucee is turning that 99 into a long, and THAT is what the web service is saying it does not support. I could be wrong.

  • Third, I appreciate that you tried using text instead, and then it said it wanted a number. There are other number types, and maybe it’s wanting it cast in a certain one.

Here is where a tool would be helpful, to tell you what the web service offers for methods and their arguments and types.

And while some editors (including CFBuilder and the old Eclipse CF extensions, and even some generic non-CFML editors) offer such web service browsing tools, there are also some online browsing tools, such as:

http://www.soapclient.com/soaptest.html

And even though WE can’t browse the WSDL over the web using such a tool, perhaps YOU can from your internal development machine, or at least from a browser on the server where the CFML is running.

Let us know what you may find.

Along those same lines, some may wonder if that method itself would really expect a username/password or rather whether you should be putting it on the createobject call itself (which supports a username/password), But as you note, the method seems to implicitly call a setter on the userid, so it seems it does expect (or at least support one). Again, a soap browsing tool will tell you for sure, and what it expects.

Then we can focus on how best to pass it what it expects, if you may not find the answer on your own with that new clarity.

Hello,
Thanks for your reply and suggestions.
I tried to find earlier today a similar workaround/solution and found a post of Ben Nadel suggesting to make SOAP requests with raw XML and manual HTTP posts.
I used a desktop tool called SoapUI 5.5.0 to test the SOAP webservices, and it gave me the SOAP envelope for the request. This I used for making the calls myself, and it seems to work.
This is what I came up with so far:

<cfsavecontent variable="soapBody">
<cfoutput>
    <?xml version="1.0" encoding="utf-8"?>
	<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:cus="http://customer.v2.webservice.projectmanagergui.xmlintl.com/">
	   <soapenv:Header/>
	   <soapenv:Body>
		  <cus:getXTMInfo>
			 <loginAPI>
				<client>******</client>
				<password>****</password>
				<userId>99</userId>
			 </loginAPI>
			 <options/>
		  </cus:getXTMInfo>
	   </soapenv:Body>
	</soapenv:Envelope>
</cfoutput>
</cfsavecontent>

<cfhttp
    url="http://10.0.0.100/project-manager-gui/services/v2/XTMCustomerMTOMWebService" method="post" result="httpResponse">
    <cfhttpparam type="xml" value="#trim( soapBody )#"/>
</cfhttp>

<cfif find( "200", httpResponse.statusCode )>
    <cfdump var="#httpResponse.fileContent#"/>
    <!--- Parse the XML SOAP response. --->
</cfif>

This give this output, which holds the requested info / data:


Now I need to find a way to parse this output.
Tried this:
<cfset soapResponse = xmlParse( httpResponse.fileContent ) />
But trows an error. The output is not really XML, sort of …

Sure, SOAPUI is another good tool.

As for switching to passing in XML, I’d warn against complicating things that way. It may not be necessary. The reason I suggested you use a tool was to see WHAT datatype was expected for that userid field.

But as for your error with parsing the filecontent, you will help yourself by just doing a cfdump of it, to see if it is even XML. It may be, but it may not. Or it may not be in a form that xmlparse knows to process (which requires an opening <?xml> directive, etc.

Ah, I am seeing that you did provide the output of that variable, though you are showing the debug output instead, right? Or if that is a dump, then notice that it’s not JUST XML. It opens and closes with --. XMLParse won’t know what to do with that.

More to the point, the XML is that from the web service, in SOAP format. Here is where I said that switching from using the CFML web service feature to pure cfhttp communications would be more work. See if Ben’s resource (or others) may tell you how best to process that result. You can’t just xmlparse it.

But really, I’d propose you instead see my last comment and try to get things working as you originally hoped. The whole point of CFML’s web service handling is to ease the burden of web service processing, making it just about CFML (as the CFML engine does the dirty work) versus you having to focus on the http communications and pure xml processing.

A lot of those articles on the web talking about “doing it yourself” with cfhttp are either a) for some strange need that regular CFML processing can’t handle, or b) from a time when earlier CFML engines had more trouble with various aspects of some web services (or versions) that were being called. Sometimes that extra work is just not warranted, as “powerful” as it may seem.

I think it is expecting an “int”, but it is given a “long”.
I’m no expert in debugging such things, but FusionReactor gave me some insight:



But how can I cast the parameter “userId” to an “int”, or force Lucee to handle it as an “int”?
XTM_getXTMInfo = ws.getXTMInfo({password="*****", userId=99, client="*******"},{});

Have you tried doing

XTM_getXTMInfo = ws.getXTMInfo({password="****", userId=javacast("int", 123), client="******"},{});

(notice the javacast("int", 123a))

In my previous life I did a lot of WebService stuff and JavaCast [https://docs.lucee.org/reference/functions/javacast.html] is your friend

Thanks for looking into this. Is the “a” after 123 a typo in this sentence ?

(notice the javacast("int", 123a) )

Yes, I’ve tried this:

XTM_getXTMInfo = ws.getXTMInfo({password="******", userId=JavaCast("int", 99), client="******"},{});

Made no difference.
Same for

<cfparam name="id" default="99" type="integer">
...
XTM_getXTMInfo = ws.getXTMInfo({password="******", userId=#id# client="******"},{});

Gunter, I appreciate that you used FR to find the indication of the call wanting an int, and it’s great that Mark shared the javacast, but since you’re still having problems applying those discoveries, would you please use one of the tools (whether SOAPUI or the soapclient svc) to let the WEB SERVICE tell you FOR SURE what datatype it expects for userid? :slight_smile:

FWIW, I had been holding off suggesting javacast simply because the FIRST focus is to find WHAT datatype it expects. You seem to be dancing all around this simple suggestion I have made from the very first reply yesterday. I don’t understand why.

Was it perhaps challenging for you to find what the tools were reporting as the datatype? Again, since we can’t access the web service remotely, we can’t tell “for you”. Someone may propose that if you share the WSDL, we could look directly at it or point our own wsdl browsing tool at that. But note that often the wsdl will include references to still other wsdl which is pulled in dynamically, so we’d again not have access to that if it’s also not publicly accessible.

Bottom line: does your view of the wsdl or use of either tool show you WHAT datatype is expected for setuserid? Since that seems to be a setter that’s called implicitly, it may not appear in the definition of that getXTMInfo method you’re actually calling, but there should be definition SOMEWHERE in the wsdl that would define it.

As always, just trying to help.

I’m sorry, you’re right. But I’m not avoiding it, I simply couldn’t find that info in the SOAPUI application.
I downloaded the WSDL file (XTMCustomerMTOMWebService.xml (266.6 KB) ), and this is specified:

  <xs:complexType name="loginAPI">
    <xs:sequence>
      <xs:element name="client" minOccurs="0" type="xs:string"/>
      <xs:element name="integrationKey" minOccurs="0" type="xs:string"/>
      <xs:element name="language" minOccurs="0" type="tns:languageCODE"/>
      <xs:element name="password" minOccurs="0" type="xs:string"/>
      <xs:element name="userId" minOccurs="0" type="xs:long"/>
      <xs:element name="username" minOccurs="0" type="xs:string"/>
    </xs:sequence>

So, it is expecting a long … Now I’m confused :thinking:

Thanks for your help, very appreciated!

Yeah, that was a typo.

Using a CFParam wouldn’t cast the 99 to anything specifically. It would just test it when passed in.

If you can send the WSDL we can see what that function actually accepts.

Can you then do a

<cfdump var="#ws#">

It will tell you what all the functions are and their params.

Hi Mark, the WSDL file was in my previous post: XTMCustomerMTOMWebService.xml (266.6 KB)

As you deducted it expects a long, so JavaCast('long', 1234) should help!

Unfortunately not, same error :neutral_face:

In case it is helpful … I had a similar problem and this was the code I used to manually make a request to the problematic webservice. I apologize that it is not commented very well but just to break it down:

‣ calls a remote webservice : http://mail.ilikeit.net/Services/svcUserAdmin.asmx (working url)
‣ notice the function is set to allow the manual entry of the variables (ex: …example.domain.com…) including AuthUserName, AuthPassword and DomainName
‣ notice the method called is GetUsers
‣ notice that after examining the webservice you can see that the result node name is: UserInfo … it would not take much for you to find your node name and required params to pass into your modified version of this function

<cfscript>
GetUsers_Result=GetSmarterMailUserData(method="GetUsers",resultNodeName="UserInfo", paramsAsXML="<AuthUserName>Administrator</AuthUserName><AuthPassword>fake_password_string</AuthPassword><DomainName>example.domain.com</DomainName>");
</cfscript>
<cffunction name="GetSmarterMailUserData">
	<cfargument name="method" default="GetUsers" />
	<cfargument name="resultNodeName" default="UserInfo" />
	<cfargument name="paramsAsXML" default="<DomainName>welikeit.net</DomainName>" />
	<cfsavecontent variable="soapBody"><cfoutput>
		<?xml version="1.0" encoding="utf-8"?>
			<soap:Envelope
				xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
				xmlns:xsd="http://www.w3.org/2001/XMLSchema"
				xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
			<soap:Body>
				<#method# xmlns="http://tempuri.org/">
					#paramsAsXML#
				</#method#>
			</soap:Body>
		</soap:Envelope>
	</cfoutput></cfsavecontent>
	<cfhttp url="http://mail.ilikeit.net/Services/svcUserAdmin.asmx" method="post" result="httpResponse">
		<cfhttpparam type="header" name="SOAPAction" value="http://tempuri.org/#method#" />
		<cfhttpparam type="xml" value="#trim( soapBody )#" />
	</cfhttp>
	<cfsavecontent variable="NamespaceCleanerXSLT"><?xml version="1.0" encoding="utf-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:output method="xml" indent="no"/><xsl:template match="/|comment()|processing-instruction()"><xsl:copy><!-- go process children (applies to root node only) --><xsl:apply-templates/></xsl:copy></xsl:template><xsl:template match="*"><xsl:element name="{local-name()}"><!-- go process attributes and children --><xsl:apply-templates select="@*|node()"/></xsl:element></xsl:template><xsl:template match="@*"><xsl:attribute name="{local-name()}"><xsl:value-of select="."/></xsl:attribute></xsl:template></xsl:stylesheet></cfsavecontent>
	<cfif find( "200", httpResponse.statusCode )>
	    <cfset soapResponse = xmlParse(XmlTransform(httpResponse.fileContent,NamespaceCleanerXSLT),false) />
	    <cfset responseNodes = xmlSearch(soapResponse,"//#resultNodeName#") />
	    <cfif arrayLen(responseNodes)>
	    	<cfreturn responseNodes>
		<cfelse>
			<!--- find a node named Message (case sensative) and select it's parent --->
		    <cfset responseNodes = xmlSearch(soapResponse,"//Message/../") />
		    <cfif Len(responseNodes)>
		    	<cfreturn responseNodes[1]>
			<cfelse>
				<cfreturn soapResponse />
			</cfif>
		</cfif>
	</cfif>
</cffunction>

Here is a snippet of my own result handler. Perhaps you can adapt it to your own use.

Note: all results have a namespace, this causes parse issues so I clean out that namespace using this transformation trick. Your use will vary.

	<cfsavecontent variable="NamespaceCleanerXSLT"><?xml version="1.0" encoding="utf-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:output method="xml" indent="no"/><xsl:template match="/|comment()|processing-instruction()"><xsl:copy><!-- go process children (applies to root node only) --><xsl:apply-templates/></xsl:copy></xsl:template><xsl:template match="*"><xsl:element name="{local-name()}"><!-- go process attributes and children --><xsl:apply-templates select="@*|node()"/></xsl:element></xsl:template><xsl:template match="@*"><xsl:attribute name="{local-name()}"><xsl:value-of select="."/></xsl:attribute></xsl:template></xsl:stylesheet></cfsavecontent>
	<cfif find( "200", httpResponse.statusCode )>
	    <cfset soapResponse = xmlParse(XmlTransform(httpResponse.fileContent,NamespaceCleanerXSLT),false) />
	    <cfset responseNodes = xmlSearch(soapResponse,"//#resultNodeName#") />
	    <cfif arrayLen(responseNodes)>
	    	<cfreturn responseNodes>
		<cfelse>
			<!--- find a node named Message (case sensative) and select it's parent --->
		    <cfset responseNodes = xmlSearch(soapResponse,"//Message/../") />
		    <cfif Len(responseNodes)>
		    	<cfreturn responseNodes[1]>
			<cfelse>
				<cfreturn soapResponse />
			</cfif>
		</cfif>
	</cfif>

Hi. Thanks for looking into this issue and your input.
Your solution is similar to one of my previous posts (CF / Lucee incompatibility for createObject() / SOAP request), but because this is kind of doing it all manual with raw XML and HTTP requests, @carehart suggested to find the problem with the CFML web service feature before making the long run with manual stuff.

Thanks. Will come in handy when the CFML web service functions are not fixable and I need to do it manually.

According to the online help for JavaCast:

ColdFusion:

Converts the data type of a ColdFusion variable to a specified Java type to pass as an argument to Java or .NET object. Use only for scalar, string, and array arguments.

Lucee:

Converts the data type of a CFML variable to pass as an argument to an overloaded method of a Java object.
Use only for scalar and string arguments.

So, meaning it is usable only for Java objects (so, not for “webservice” objects), and only for scalar and string arguments.

So JavaCast() holds no possible solution here anyway, correct?