[Looking for Charlie's main web site?]

Still more reasons to make sure you have updated your ColdFusion 10 web server connector

Several weeks ago, I did an entry, CF911: Why/when you MUST update the web server connector for #ColdFusion 10, and may have missed it.

In this entry, I want to throw in another reason why it's important to make sure you properly update (reconfigure/rebuild/upgrade) your web server connector after applying certain CF10 updates, or if applying only the latest update for the first time to a newly installed CF10 instance.

[....Continue Reading....]

Using tools to browse Web Services. Great for solving problems

I just blogged about a feature in CFBuilder that allows you to browse web service methods, arguments, etc., like the same feature in Dreamweaver and the Adobe Eclipse Extensions for Eclipse.

But what if you have none of those tools, or just don't care to fire them up?

Many alternative tools for browsing web services

The good news is that there are various free and commercial tools, some you download and some entirely web-based (and again, free) that do basically the same thing (and more), letting you see the methods, args, etc.

I list several of them in a web services tools category at my CF411 site, which now has over 1000 tools and resources of interest to CFers, in more than 125 categories. (As tempting as it is, you may want to wait and finish this entry before you go get lost in the treasure trove of goodies there.)

Useful when you're fighting a web service

These web service browsing tools (and the editor features) can all be great when you've been given a web service and either you don't know what the methods or arguments are, or you're having problems understanding why something's not working. Sure, you can just browse the URL and get back the WSDL XML, but these tools (like the editor features above) do that for you and present the information in a far friendlier interface.

Indeed, some of these other tools (including some free web-based ones) even go a step farther and let you even input values for the arguments and execute the web services without writing any code. That can really help confirm if and how a web service should work, if you're struggling with it in CFML.

A great example: SoapClient

A great example of a free online tool that both browses and executes web services is SoapClient. Couldn't be more straightforward, and no registration required. Check it out.

More CF-based web services tips

Finally, I'll note as well that as you work with web services in CFML I also have several other blog entries from the past with some things that may surprise you, all accessible in my web services category.

CFBuilder tip: Browsing web services--did you know you can? and a bug you may hit

Did you realize that you can browse web services in CFBuilder (just like you could in Dreamweaver or the Adobe ColdFusion 8 Extensions for Eclipse), seeing all the methods, their args, etc.? I'll show you how, but I'll also warn of a bug you may hit in the beta, and give a simple solution.

This is another in a series of tips I want to start share for those working with CFBuilder. While oriented mostly to those who had not used (or stuck with) CFEclipse before, this one may surprise even folks who had used it.

How to browse web services in CFBuilder

Just as with the older Adobe Eclipse extensions for CF8, the ability to browse web services in CFBuilder is in the same Services Browser interface that one can use to look at CFCs.

It's easy to miss, even if you do know about the Services Browser (Window>Show View>Services Browser). Look in the top right corner of that view, and there's a pair of icons which when you mouse over them allow you to switch between "Show Web Services" and "Show CFCs on RDS Server". Nifty.

So just as in DW, you can then add the WSDL URL for any web service, either by clicking the red circled plus sign in the top left of the view, or by right-clicking on whitespace in the view and choosing "add wsdl".

It will then retrieve the web service description information and display in a friendly tree view all its services, their methods, and those methods arguments and datatypes, etc. (see the screenshot above).

This really can make it so much easier to work with web services, especially ones you don't know or are having trouble with.

But wait, there's more. Call now and receive .... (sorry) ...

Seriously, though, this is nice if you didn't know about it. The tool can even build code for you to call the web service. As with Dreamweaver, you can right-click on a method and have the editor build for you your choice of either CFINVOKE or creatobject code to call the web service and that method. It will even populate a placeholder for the required arguments for you. Talk about service with a smile. :-)

(As a bonus, I'll note that there are also several other tools, free and commercial, some web-based and some downloadable, that can also be used to browse web services. More on that in another entry I've now posted.)

One gotcha, for now: stuck on "contacting server", and easy workaround

The following concern was fixed by the final release.

There IS a gotcha. (There's always a catch, right?) Actually, since CFBuilder is still in beta, we can hope this will be fixed soon.

When you first add a web service URL, and you then try to expand it to see its services and methods, sadly it will seem stuck reporting "contacting server" where the service names and methods should be. Grr.

Oddly, when I saw that, I moved on thinking it was a problem with that web service, and when I added another WSDL URL, suddenly the one that was hung now worked. I though it odd, till I noticed it did that with EVERY URL I added.

So what's the solution when you add one and it hangs like that? Just add another. You could even just add the same one twice (and then delete the dupe you will now have).

It also "fixes itself" if you delete one of the listed WSDL URLs.

While you might think a restart of CFBuilder (the "workbench", in Eclipse parlance) would also help, actually it hurts. When you restart it, and try to open a web service again, it hangs. You have to repeat the process above. Worse, you need to do it for any web service you try to open. That's an annoyance, for sure.

But here's some "good" news. I just confirmed that you don't even need to enter a valid WSDL URL (or even a valid URL). I just added "x" as a new service, and it got the hung services working. One may argue it shouldn't allow that, but for now that's a blessing. :-)

I've opened a bug report for the problem of the "hanging" web service feature. If you'd like to vote for it, you can find it as bug id 79321.

More to come, here and in my daylong CFUnited class, "Getting Started with CFBuilder"

If you're looking for more such CFBuilder tips, keep an eye here. More than that, if you'd like to spend a day getting immersed in an introduction to CFBuilder from the perspective of one coming from other editors, that will be the focus of my daylong class being held on-site the day before CFUnited, August 11. It's one of several such daylong classes being organized by (and available for purchase separately from) CFUnited.

More on the class in my previous blog entry or at the class detail page.

CF8 Hidden Gem: New option to save java source for web service proxy--with createobject only

Have you ever wanted to see the Java source code for the proxy/stub that's created when you invoke a web service from Coldfusion? Well, here's a hidden gem in CF8 (one of dozens I discuss in my "hidden gems in cf8" talk) that does just this.

Curiously, it's only available when you invoke a web service using createObject(), not CFOBJECT or CFINVOKE.

It's enabled using the new ArgStruct argument that I discussed last month.

<cfscript>
wsargs = structnew();
wsargs.savejava="yes";

convert=createobject("webservice","http://www.webservicex.net/CurrencyConvertor.asmx?wsdl",wsargs);

writeoutput(convert.ConversionRate(FromCurrency='USD',ToCurrency='EUR'));
</cfscript>

You may wonder why you have to put it in this argStruct when it's the only key being put in the structure. That's just the way it is. Of course, I could have created the structure using the new implicit array creation syntax, as in:

wsargs = {savejava="yes"};

which replaces 2 lines with 1.

For those who don't care for CFSCRIPT

Of course, you don't need to use CFSCRIPT to use createObject, for those not comfortable with it. I could just as well have done it all in tags, as:

<cfset wsargs = structnew()>
<cfset wsargs.savejava="yes">

<cfset convert=createobject("webservice","http://www.webservicex.net/CurrencyConvertor.asmx?wsdl",wsargs)>

<cfoutput>var="#convert.ConversionRate(FromCurrency='USD',ToCurrency='EUR')#</cfoutput>

Where the Java source is placed

So where is the Java placed? In the same directory where the java proxy stubs have been placed since CF6: [coldfusion]/stubs/. In the case of the standalone version of CF8, that might be c:\coldfusion8\stubs.

Each invocation of a web service in CF (whether you use the saveJava option or not) will create a directory there, typically in a form like WS729914123 (one for each separate web service invoked by any CFML requests), and within the subdirectories of that directory you'll find class files reflecting the name of the called web service.

If you don't use the saveJava option, you'll see only class files. If you'll see corresponding .java source files for each.

Finally, note that the Java source files will be removed automatically if the web service is refreshed (manually or in the CF Admin) and you call it without the SaveJava option (which also means if you invoke it using CFOBJECT or CFINVOKE).

(*Update*: In the original entry, I said the source would be removed if you called the web service without the SaveJava option, but I should clarify that it's if you do that and you cause the web service to be refreshed, not just any call, since that would use the compiled result of the earlier call unless you told it to do otherwise.)

Still, for those who have long wished to better understand these Java proxy stubs, it's nice that we have the option to see the source if we want to.

Still more to come

There's still more power in CF8 for those that like to tinker with the java proxy/stub generation. More on that in a later entry.

CF8 Hidden Gem: New ArgStruct argument for createObject with web services

Here's another hidden gem in CF8: did you know that there's a new optional "ArgStruct' argument for use with createObject(), when using it to invoke web services?

Following on my previous note about a new RefreshWSDL option in CF8 for CFINVOKE and CFOBJECT, I mentioned there that it was also an option in the createObject() function, but naturally it can't be passed as a tag attribute like with those above.

Instead, it's enabled using this new ArgStruct argument. Technically, it's not "named" ArgStruct but rather it's simply a new optional 3rd argument you can specify when invoking a web service (the term "argStruct" simply comes from the CF docs for the function, where it refers to it by that name. (While yo umay notice that the docs indicate this also allows setting a timeout for the web service invocation, note that that only times out the requesting of the WSDL, not subsequent method calls against the object.)

Anyway, in that structure you create, you simply define RefreshWSDL as a key within it, all of which is passed into the createObject() function as that 3rd argument:

<cfscript>
wsargs = structnew();
wsargs.refreshwsdl="yes";

somevar = createobject("webservice","http://[server]/[webserviceurl]",wsargs);
...
</cfscript>

Of course, you could just as easily do all the above in 3 CFSET tags. It doesn't matter. The key is the addition of the 3rd argument to the createObject(). And it doesn't matter at all what you call the structure (I named mine "wsargs").

Now, you may think this approach seems clumsy, and ask, "why didn't they just permit the refreshWSDL itself as a new argument on the createObject()?". It's a fair question.

But it turns out there's actually a little more to this new ArgStruct option, and it's different enough that I'll talk about it in a separate entry.

CF8 Hidden Gem: Refreshing Web Service WSDL and CF proxy/stub with new RefreshWSDL option

One of the many hidden gems in CF8 (and I've found dozens of them) is a new attribute on CFINVOKE or CFOBJECT (and argument for createObject) called RefreshWSDL. It's a another solution to the long-standing problem of invoking web services whose metadata may have changed since previous executions. I'll explain the older approaches, some new in CF 6 and 7, later here for those who missed them.

So what's the problem it solves? If you have CFML code that calls a web service, and one day it just stops working, the problem may be that the web service itself has changed. Perhaps the owner changed the return type or some other metadata.

The new solution allows you to refresh that WSDL on the CFINVOKE or CFOBJECT tags, or the createObject method.

Doing it in CFINVOKE/CFOBJECT

Here's how to do it in CFINVOKE.

<cfinvoke webservice="http://[server]/[webserviceurl]" method="[methodname]" refreshWSDL="yes" ...

Adding it as an attribute for CFOBJECT would work essentially the same way, for those familiar with that tag.

Doing it in createobject()

Doing it in the createObject() function, however, is quite a bit different and leverages some new syntax for that function. I'll show that in another blog entry and will point out another new feature for that function.

There are a couple more points to consider about this, but first I just want to explain why it's needed, for those who haven't heard of such options.

Why should you have to refresh the web service metadata?

Just to back up for a moment, the problem stems from CF's attempt to help. On the first request for a given web service, CF does some caching to make future requests go faster, not caching the results of the web service method but rather the artifacts used by CF based on the description of the web service itself.

CF uses the web service description (WSDL) reported at the time of that first call to create a java proxy/stub based on that, which it then reuses on future calls from CF to that web service.

The issue arises if/when the web service metadata changes. CF won't know, and will continue to use the older cached proxy/stub, and your long-running code may fail if it doesn't match the new WSDL returned by the web service.

So we need a way to tell CF to refresh its cache of that proxy stub.

This new feature is certainly the easiest way to make that happen, but it's not the only way.

Not the only way to refresh the cache, but the easiest

Some may know (and I've written previously) about two programmatic ways to refresh the proxy/stub, whether you're using CF7 (which added a new method in the Admin API) or using CF 6 or above (using an undocumented/unsupported service factory method), as well as an available button in the CF Admin console that could do it (since CF6).

A benefit of this new approach is that it doesn't require you to know the CF Admin password.

Easier, yes, but could be used inappropriately

Of course, with power comes responsibility. You don't want to leave this indicator in your code for all requests, such as in production. That would force CF to do extra work on each web service invocation, defeating the whole purpose of the caching. It's like the tools CFLOG or CFTRACE. Well, more like the former. At least the latter has an Admin console option to disable it even if left in production code.

It's one of those things where opinions will differ. On the one hand, the ease of mistakenly leaving this in to get into into production could make one argue that it ought not be in code, or at least should not be in code calling the web service but rather code to manage the cached stub itself, which is what the previous features did.

On the other hand, those required admin access to perform (except for the unsupported servicefactory approach). Similarly, even if there WAS an option to disable refreshwsdl in production, you'd be stuck if you needed to refresh the cache in production and had no admin access.

At least we have the choices now, and forewarned is forearmed.

Finally, as for more CF 8 hidden gems, I'll note that I've got a user group presentation on the topic, and I have a few dozen more I share. I'll start sharing more of those in blog entries.

Reloading CF web services programmatically, using the CF7 Admin API

I'm surprised to not see much out there about how to reload or refresh CF's cached WSDL proxy for calling a web service, at least programmatically using the new CF 7 Admin API. Perhaps it's because people have been tripped up, or simply haven't explored it. Either way, I'd like to offer here the code you need, and also point out some tips and traps.

Update for CF8: As an update to this entry from 2006, which is focused on CF7, I'll note that there was yet another approach that was added in CF8. Both still work and have their own value. More on the CF8 feature in my later blog entry. Still, the feature added in CF7 is important to understand, too, so please read on.

Introduction: Why You Would Want to

As background, someone reported having a problem calling a web service from CFML, and a solution suggested was that the person reload or refresh the web service using the CFMX Admin console (in the "Data & Services" > "Web Services" nav bar tab. There, you'll find any web services that have been called from CF (which is often a surprise to folks that they're tracked there). For each listed web service, there are 3 buttons and the middle one does a refresh (which means it goes and grabs the WSDL and builds a local java proxy stub, which is used when you then invoke the web service).

But one may then wonder how to do that programmatically, without having to open the Admin console. In CFMX 6.1, you could use the undocumented ServiceFactory, as I'll show below to those still using that. But since that's being deprecated, you really ought to learn the new CF7 approach.

The CF 7 Admin API Approach

As of CFMX 7, we are expected to use the new Admin API, a set of CFCs provided with CFMX 7, which offer a formal, secured API for accessing functionality otherwise offered in the Admin console.

So how would one do that to refresh a web service? Well, there is an extensions.cfc in the Admin API (a set of CFCs in the webroot's /CFIDE/adminapi/ directory), and it has a reloadWebService method that's just the trick. How did I know that? You can call the built-in CFC documentor by browsing the CFC directly:

http://<em>[servername]</em>/cfide/adminapi/extensions.cfc

Before you can call that, though, note that you do need to "login" to the Admin API by calling the login method of the administrator.cfc first. (Check out its docs to learn more.)

But to save you that effort, here's some code. (As always there are several ways to call a CFC and its methods (using CFINVOKE or CFOBJECT, or createObject either within CFSCRIPT or not), but here's at least one approach.):

<cfset createObject("component","cfide.adminapi.administrator").login("youradminpw")>
<cfset ws = createobject("component","CFIDE.adminapi.extensions")>
<cfset ws.reloadWebService(name="<em>webservicename</em>",path="<em>WSDLurl</em>")>

Note that you need to specify your own admin password in the first line, and in the last line you need to specify a web service name and its WSDL URL.

What's with this notion of passing in a web service "name"?

As you contemplate that code, you will certainly know what the WSDL URL is, since it's the same one you'd use in a CFINVOKE or CFOBJECT/createobject call of the web service itself. But what's the "name" requested here? Well, that can trip you up and it deserves further discussion, as it has several ramifications as I'll explain here.

The name is the name shown in the Admin console for the given web service. The trick/trap is that if you never open and change the Admin console entry for this web service, then the name will simply be the same value as the WSDL URL. But there's more to understand.

First, if you didn't know it, one can edit that "name" in the Admin console, and then one can even use that "name" as an alternative (or "alias") to the web service WSDL URL when invoking the web service from CFML. That's a whole separate subject which I've covered in user group talks in the past.

But assuming that no one has modified the web service name (or for reasons I'll explain in a moment, if you are not using such an alias name when you invoke the web service), then you can presume the name and WSDL URL to be the same. As an example, one could change the last line above to:

Getting Web Service Names Programmatically

Now may wonder, "can I get the web service name programmatically?" You can. But here's where it gets a little confusing. There is an available getwebservices method of the extensions.cfc. And according to the docs, you can either pass in the "name" of the webservice, or leave it off to get all web services. If we don't know the name, then we may think we'd want to use the latter approach. But I find that it doesn't quite work as straightforwardly as it seems.

First, I tried calling getwebservices() without a name:

<cfset createObject("component","cfide.adminapi.administrator").login("youradminpw")>
<cfset ws = createobject("component","CFIDE.adminapi.extensions")>
<cfdump var="#ws.getwebservices()#">

Sadly, it returned an empty dump as a result. Yet I had several web services listed in my admin console. Here's the thing: none had a name. I then renamed one of them (to "test"), and tried it again, and suddenly the call did return an array of structures (1, in my case) with the name and WSDL URL.

Hmm. So it seems instead that the getwebservices() ought perhaps instead be named getnamedwebservices(), since it only returns web services whose names have been changed (been given an alias).

Still, though, if I do pass in a name, as the docs suggest, then I do indeed get the same result:

<cfset createObject("component","cfide.adminapi.administrator").login("youradminpw")>
<cfset ws = createobject("component","CFIDE.adminapi.extensions")>
<cfdump var="#ws.getwebservices("test")#">

Now, you may wonder: "if I can't a listing unless I know the name, or can only get a list of 'all' of them if they are named, then how might I get the info for ones that have no name or whose name I don't know?"

Good question. And guess what I've found? A couple of important things.

First, I've found that you can also pass in the WSDL URL, even for a web service that's been renamed with an alias like "test", such that this works:

<cfset createObject("component","cfide.adminapi.administrator").login("youradminpw")>
<cfset ws = createobject("component","CFIDE.adminapi.extensions")>
<cfdump var="#ws.getwebservices("http://ws.invesbot.com/stockquotes.asmx?WSDL")#">

Again, that's not the web service name but the WSDL URL. And the resulting dump shows the name and that URL. So the API docs on this are a little misleading.

Be sure to refresh the "right" webservice: the one you'd really try calling

But perhaps a more important thing is that I found that you can have a web service entry for the SAME WSDL URL but with different names/aliases. Why is this important? Because you want to make sure you refresh whichever one you're using.

This goes back to my point above when I introduced the refreshWebService method: you need to give it the name as YOU call the web service, otherwise you'll be refreshing a different proxy stub and won't see the benefit you expect.

If you use the WSDL URL when you invoke the web service, then that will create a proxy stub with that "name", and therefore you want to use that as the "name" when you refresh it.

If you rename a web service in the Admin console, and then use that when you invoke the web service, then you want to use that as the "name" when you refresh it.

Refreshing Web Services Using the ServiceFactory

Since some folks reading this may not have moved to CF 7, let me show how you could do the same using CFMX 6/6.1 (or indeed 7, since it still works there). It's important to note, however, that using the ServiceFactory is not only not documented but it's also not supported. It has security problems (since there's no need to provide the admin password as you must in the Admin API). Also, it may eventually be obsoleted or otherwise restricted.

Still, since it's been documented by others in the past and is readily available on the web, I'll offer it here:

<cfobject action="CREATE" type="JAVA" class="coldfusion.server.ServiceFactory" name="ServiceFactory">
<cfset ServiceFactory.getXMLRPCService().refreshWebService("<em>webservicename</em>")>

Now, you'll notice that I've indicated that the value passed into the refreshWebService method is the webservicename. That's because it works just like the Admin API reloadWebService discussed at the top here. Be sure to specify the name as you would use it in the invocation of the web service (whether an alias or the full URL).

How to Confirm That Refreshing is Working

As you try these three approaches (Admin button, Admin API, and ServiceFactory), you will probably benefit greatly from being able to see for sure that the refresh/reload is updating the java proxy/stub class files. Where do you find them? They're stored in a "stubs" directory under your CFMX install directory, such as C:\CFusionMX\stubs or C:\CFusionMX7\stubs.

Then, under that, you will find directory names such as WS141836989, or a name that's the same as the alias/name you give to a web service in the Admin console. Inside those directories you will find other subdirectories, eventually finding some that hold the .class files representing the objects available in the web service. It's those .class files whose date/time stamp you want to see changing when you do a refresh/reload.

I'll note that there's no mapping or indication of that WSnnnn directory name, to know which one holds the web service you're interested in. I guess you just have to find the right one by looking for one whose class names map to the web service object you're calling. (If anyone knows a better connection, please do share it.)

Finally, I think it may be worth clarifying that when you do a refresh/reload of a web service using the approaches above, you need to have first made a call to that web service from CFML (or entered it manually in the admin console).

Hope all that's helpful.

Summary of Notes for Adobe Folks

Before I end, in case any Adobe folks are listening, here is a restatement (and expansion) of the couple of observations I made about wrong or undocumented functionality. This is for both the docs group and the engineers, since this is also about internal API documentation returned by the CFCs and their functionality:

  • getwebservices() only returns web services that have a name that was changed, whereas the API doc says it returns "all web services", so it either is working incorrectly or ought to be called getnamedwebservices instead
  • though not documented, getwebservices("wsdlurl") works also. The API docs say that it should only take a name (and I tested this against a web service where I had renamed it, so it was not getting it "by name")
  • if you do consider renaming the getwebservices() method to be called getnamedwebservices(), you might then also want to rename getwebservices(name) to getwebservice (singular), since it just gets one webservice
  • it would be nice to be able to refresh ALL webservices that use a given WSDL URL at once, perhaps by new method that accepts URL rather than name (and works for all occurrences of that URL in the cache, whether named with an alias or not)

Handling optional arguments to web service method calls in CFMX 7

Have you tried to invoke a web service method from CFML when it declares optional arguments, and you don't pass in those optional arguments? You get an inobvious error message. Well here's good news: there's a new solution in CFMX 7, with CFINVOKEARGUMENT's Omit="yes|no". Very nice.

Thanks to Steven Erat for pointing this out.

For those who have seen my presentation on CFML web services (at CFUNited and over the past few years in other venues), this has long been a sore spot for me. So glad to see it fixed.

BlogCFC was created by Raymond Camden. This blog is running version 5.005. (Want to validate the html in this page?)

Managed Hosting Services provided by
Managed Dedicated Hosting