Webservice Producer <cfcomponent>
<cffunction name="test1" access="remote" returntype="string" output="No">
<cfargument name="a1" required="Yes">
<cfargument name="a2" required="No" default="2">
<!--- no method implementation for test --->
<cfreturn "It worked!">
</cffunction>
</cfcomponent>
Now consider how you might consume the webservice while only passing the required argument a1:
Webservice Consumer (Typical Syntax)
Webservice Consumer (Alternate Syntax)
The problem with each of these is than an error will occur which seems completely contradictory, hinting that the webservice method could not be found.
Web service operation
"test1" with parameters {a1={1},} could not be found.
The error occurred in /opt/coldfusionmx/wwwroot/serat/webservice_client.cfm: line 11
11 :
<cfobject webservice="http://localhost/webservice_optargs.cfc?wsdl" name="ws"> 12 :
<cfinvokeargument name="a1" value="1"> 13 :
<!--- <cfinvokeargument name="a2" value="2">---> coldfusion.xml.rpc.ServiceProxy$ServiceMethodNotFoundException: Web service operation
"test1" with parameters {a1={1},} could not be found.
at coldfusion.xml.rpc.ServiceProxy.invoke(ServiceProxy.java:136)
In brief, this is a Java "method not found" exception. Yet if you simply uncomment the second cfinvokeargument for a2 suddenly the webservice will work. I'll try to explain why the error occurs and how to solve this problem ...
When ColdFusion consumes a webservice the first time it initially generates a "stub" which acts as a Java interface to the remote webservice, according to the WSDL specification. The stub is composed of multiple Java classes, and one of those classes contains a method which corresponds to the webservice method and its arguments. In this example, the stub interface (found in CFusionMX7/stubs/) contains the following method signature:
public String test1(Object a1, Object a2)
Under the hood, the cfinvoke consumer syntax with just one cfinvokeargument subtag (omitting the optional argument) is translated to a Java method call test1(a1), and since a Java method is partly defined by the arguments that it takes the JVM then throws an exception because the method signature is actually test1(Object a1, Object a2) not test1(Object a1). Hence, the ServiceMethodNotFoundException.
The solution for this problem was introduced in ColdFusion MX 7.0 with an optional attribute omit="(yes|no)" for the cfiinvokeargument tag. When calling the example webservice and passing the required argument a1 only then you must use a <cfinvokeargument name="a2" omit="yes"> without a value to properly invoke the webservice. Effectively, this causes ColdFusion to make the Java method call test1(a1, a2) where a2 is null, and this matches the the method signature test1(Object a1, Object a2).
The CFML invocation could include some logic to determine how to properly call the webservice under the conditions where optional arguments may not be sent:
Webservice Consumer (Corrected Typical Syntax)
<cfset variables.arg1 = 1> <!--- cfset variables.arg2 = 2> ---> <cfinvoke webservice="http://localhost/webservice_optargs.cfc?wsdl" returnvariable="result" method="test1"> <cfinvokeargument name="a1" value="#variables.arg1#"> <cfif isdefined("variables.arg2")> <cfinvokeargument name="a2" value="#variables.arg2#"> <cfelse> <cfinvokeargument name="a2" omit="yes"> </cfif> </cfinvoke>
Unfortunately, the syntax to pass optional arguments as nulls does not exist for the tag CFOBJECT or the function createObject().
From the cfinvokeargument documentation:
You can omit a parameter by setting the cfinvokeargument omit attribute to "yes". If the WSDL specifies that the argument is nillable, ColdFusion MX sets the associated argument to null. If the WSDL specifies minoccurs=0, ColdFusion MX omits the argument from the WSDL. However, CFC web services must still specify required="true" for all arguments.
omit Optional attribute, default is "no"
Enables you to omit a parameter when invoking a web service. It is an error to specify omit="yes" if the cfinvoke webservice attribute is not specified. "yes": omit this parameter when invoking a web service. "no": do not omit this parameter when invoking a web service.