Thursday, May 24, 2012
Rancho - code coverage tool for ColdFusion
It still has some limitations (covers only tags for instance), but it is a good start and hopefully will be enhanced to become a full fledged tool.
Friday, June 18, 2010
ColdFusion Survey
Now is your chance to influence what goes in to the next version!
Wednesday, June 10, 2009
ColdFusion 8 Performance Tuning
Tuesday, May 12, 2009
Brendan Meutzner on LCDS and CF
Thursday, April 23, 2009
Make ColdFusion 8 work with Apache CXF
If anyone is interested, I have been able to make ColdFusion 8 work with Apache CXF, which supports a variety of web service standards and libraries - SOAP 1.1, SOAP 1.2, REST, etc.
Caveats: CF8 works with CXF in the sense that CXF objects can be instantiated as Java objects in CFML and their operations successfully invoked. It does NOT work as a native CF web service call using
Detailed steps:
- Download the latest CXF distribution and extract/install it somewhere. If your CXF root is /foo, then find /foo/lib. You'll see a bunch of JAR files, and your mission is to get CF to recognize these files and load the classes in them.
- Stop the CF server.
- CF8 and the JRE that comes with it use an older, incompatible version of the JAXB library, so we need to get the newer one in place. Create /foo/lib/endorsed and copy the jaxb-api-version.jar file into that folder.
- Find jvm.config in your CF installation and open it in a text editor. Append the following to the JVM arguments: -Djava.endorsed.dirs=path_to_foo/lib/endorsed -Djava.protocol.handler.pkgs=javax.net.ssl. Find the string -Dcoldfusion.classPath= in the JVM arguments and place the path to /foo/lib immediately after the equals sign (i.e., at the FRONT of the classpath), with a comma to separate it from the rest of the classpath entries. Do not set the classpath using the CF administrator. It will not permit adding elements to the front of the classpath, and will in fact overwrite the classpath if it is used to change Java settings at any future point.
- A suspected bug in the CF classloader causes the wrong part of the CF architecture to load the SAAJ classes. [Tom Here: This isn't a bug, CF maintains absolute control over which classes it loads via its own classloader. A better workaround would be to edit the jrun.properties file in the WEB-INF/lib/cfmx_bootstrap.jar file and change the exceptions list] Get around this by copying /foo/lib/saaj-api-version.jar into {java.home}/lib of your CF installation. Then delete or rename saaj-api-version.jar in /foo/lib so it does not get loaded from that location. Also delete or rename saaj.jar from the main CF library location (ColdFusion8\lib on a Windows installation, standalone configuration).
- Disable the native JAXB library in ColdFusion by deleting or renaming jaxb-impl.jar in the main CF library location.
- Restart the CF server.
With these steps, you will be able to invoke Java objects that serve as clients for the web service endpoints. These Java objects need to be created, compiled, and installed to a location in the CF classpath in order to be invoked. The CXF documentation describes ways to create the Java objects; wsdl2java might be your best bet. You can have it create classes that are SOAP 1.1 and SOAP 1.2 compatible, allowing you to call those services via the Java objects from inside ColdFusion files.
After doing this, I did a cursory test of the native CF web service functionality (Axis 1.1) and it seems to work still, so existing code shouldn't be affected.
There ya go, I would be interested in posting an update if Alan or someone else figures out what the classloading issues with the SAAJ libraries are.
Thanks Alan!
Friday, August 29, 2008
Invoking Flash remoting requests using the Java AMF Library
Here is how you do that in Java:
Object[] args = ...
String sourceName = "my.cfc.path.Component";
RemotingMessage message = new RemotingMessage();
message.setMessageId(flex.messaging.util.UUIDUtils.createUUID());
message.setOperation("myCfcFunctionName");
message.setBody(args);
message.setSource(sourceName);
message.setDestination("ColdFusion");
Object returnValue = amfConnection.call(null, message);
and accessing the body of the response like this.
Object body = ((AcknowledgeMessage) returnValue).getBody();
Not that the "args" variable is an array of the arguments you are passing to the CFC function. It can also be a Java List, or a simple object. See the BlazeDS source for RemotingMessage.java for details. This sends the same kind of message that the mx:RemoteObject tag does in MXML, so in ColdFusion terms you are using the "Flash Remoting Update". If you use the 'raw' AMFConnection API, you would be using the "classic" Flash Remoting. Both will work, but I recommend using the RemotingMessage style.
What does this do for you? Well if you are exclusively using Flex as a client, not much. But if you would like to write Java code to invoke a CFC, this alows you to do that very easily.
Friday, July 11, 2008
Setting optional parameters in the SMS Gateway
A few notes: If the gateway receives optional parameters in a message, it will be included in the data struct that is returned to the onIncomingMessage CFC function under the "optionalParameters" key. CFDump to a file or console is your friend in these cases.
If you do not format the key (has to be able to be parsed by the Java Short.decode(String) function) or the value (must be a byte[], which in CFML is a "binary object") correctly the setting is silently ignored.
This functionality has been in the SMS Gateway since 7.0.2. It is not in the 7 or 8 documentation, which is bad, but we will correct this moving forward.
Wednesday, July 02, 2008
Special Axis types and ColdFusion
He sent me the WSDL and his test and everything looked OK. It was a pretty complex input to an operation (names changed to protect privacy):
public com.example.RegisterResponse register(com.example.RegisterRequest parameters)
The RegisterRequest was a JavaBean that included the following members:
private java.lang.String sessionID;
private com.example.ThingyType importantThing;
private com.example.ThingyType otherThingy;
private com.example.ThingsILike likeList;
private org.apache.axis.types.UnsignedInt duration;
Fun stuff. Sean had done all the right things creating CFML structs that matched each of the JavaBean types (ThingyType, and ThingsILike). ThingsILike was interesting because it contained a single item that was an array. His code was right on the money here, notice that the ThingsILike object had a single member named "things" that was the array. Here is what he did and (rightfully) expected to work:
thing1 = { name = "bobby", location = "Portland" };
thing2 = { name = "sally", location = "Boston" };
iLike = { things = ["Bunnies", "Kittens", "Koalas", "ColdFusion"] };
args = { sessionID = 0, importantThing = thing1, otherThingy = thing2, likeList = iLike, duration = 60 };
Bonus points to Sean for using the new ColdFusion 8 syntax to create arrays and structures. So the array wasn't the problem. What was? Well you may have noticed that duration is listed in the Java function as being of type org.apache.axis.types.UnsignedInt. this is because the XMl Schema type in the WSDL says that the number is an Unsigned Integer:
<xsd:element name="Duration" type="xsd:unsignedInt" />
As an aside here, XML Schema has a lot of different types that elements can be. Things like NonNegativeInteger, nonPositiveInteger and other slightly wacky things. Java on the other hand doesn't have any of these. So the Axis folks (which included me) came up with a set of classes that would enforce the limitations of the Schema types and allow you to 'round trip' a service that you generated from a WSDL, then deployed and allowed Axis to create the WSDL from the Java. This is good, and when you are writing Java directly against the WSDL2Java generated code no big deal because you can pretty quickly notice that you need one of these types and make one.
But back to ColdFusion. In order for the "60" given in the code to turn in to the class UnsignedInt, ColdFusion has to be smart about how to construct this object. It's not. However there is a simple work around - create the object yourself. Here is how that would look:
duration = CreateObject("java", "org.apache.axis.types.UnsignedInt").init(60);
Notice that I call init(60), which invokes the constructor of the class. Setting this object as the value of duration in our argument structure works like a charm. So problem solved, and I hope that we can make CF smarter about these types in a future release, but encountering these XML Schema types in the wild is pretty rare so I don't expect you will need this workaround much if at all.
Using WSDL2Java to figure out CFML arguments to a Web Service
I encourage you to read the reprint of Consuming Web Service complex types in ColdFusion which goes in to some great detail on how to figure this out. This posting is going to be the cheat sheet version of that.
First, if you can't figure out why CF gives you an error about not finding the right function to invoke, you probably aren't passing in the right arguments. How do you figure these out? You run the WSDL2Java command in Apache Axis, and take a look at the Java code CF is trying to invoke. Here's a batch script for Windows and ColdFusion 8:
set CFLIB=c:\ColdFusion8\lib
set CLASSPATH=%CFLIB%\axis.jar;%CFLIB%\wsdl4j-1.5.1.jar;%CFLIB%\log4j-1.2.12.jar;%CFLIB%\commons-logging.1.0.4.jar;%CFLIB%\commons-discovery-0.2.jar;%CFLIB%\xercesImpl.jar;%CFLIB%\xml-apis.jar;%CFLIB%\saaj.jar;%CFLIB%\jaxrpc.jar;%CFLIB\..\runtime\lib\jrun.jar
java org.apache.axis.wsdl.WSDL2Java %1 %2 %3 %4 %5 %6 %7 %8 %9
Save this as "wsdl2java.bat". This will also work with ColdFusion MX 7. You will need to change the log4j-1.2.12.jar file to be just log4j.jar.
Change to an empty directory. Wsdl2Java is going to spew directories and files all over the current directory, so I find its best to create something like c:\temp\x and run the command from there. Otherwise you can give the -o option and specify the output directory.
wsdl2java.bat -v -o c:\temp\x http://example.com/path/to/wsdl
The -v is the verbose argument. I like to see all the files that are being created.
Now you can start browsing through the Java files and see what we have to work with. Look for a file named the same as the "portType" name in your WSDL. For instance, if your WSDL contains a portType that looks like this:
<wsdl:portType name="CodexWS">
Then you would look for CodexWS.java. This will be the interface of the service, and you should be able to see each of the operations in your service, and the Java objects that ColdFusion will be trying to create to call them.
How do you create the CFML to pass to the operation? Simple. Most likely there will be JavaBeans created from the XML Schema in the WSDL. These aren't that big of a deal, basically they are structures with name/value pairs, which is why you will need to make a CFML structure with the same names in it.
Check out this Bean:
public class AddThingRequest implements java.io.Serializable {
private java.lang.String manifestfile;
private java.lang.String uri;
private java.lang.String certlevel;
private java.lang.String statusname;
private com.adobe.Ldapcredentials ldapcredentials;
...
There would be a getManifest() and setManifest() function in the bean also, along with a get and set function for each one of the other bean properties. These only matter because this is what CF will look for when it finds a "manifest" entry in your CFML structure. So what would we pass in to an operation that takes an "AddThingRequest" object as an argument? Something like this:
atr = StructNew();
atr.manifestfile = "foo";
atr.uri = "uri:gimme";
atr.certlevel = "high";
atr.statusname = "Alert";
art.ldapcredentials = mycreds;
But wait, you say, what is "mycreds"? Well, the process just repeats here - we would go find the com.adobe.Ldapcredentials Java object, look at the properties it contains and make a CFML structure (mycreds in this example above) that matches it.
So that's all (ha!) there is to it. You now have the magic to invoke almost any web service out there. This isn't exactly "making the hard stuff easy" (that would be that WSDL2CFML tool I haven't written yet), but once you understand how the process works, you should very rarely have to resort to grunging through the Java code generated from a WSDL to construct the right CFML inputs. My opinion is that Web Services should be designed to present an easy to use interface, simple inputs with only reasonably complex outputs. I know that isn't the case for many services out there that you are forced to used. But with this technique you should be able to easily figure out how to use the more complex ones.
Wednesday, May 28, 2008
MAX 2008 registration is now open
MAX is the descendant of the Allaire ColdFusion Developers Conference, which turned in to the Macromedia DevCon, which morphed in to Macromedia MAX and is now Adobe MAX. This year it is going to be in San Francisco, which will be great. Not as great as MAX 2006 in Las Vegas IMHO, but SF is a fun place to visit. :-)
I will be giving a session on Livecycle Data Services and BlazeDS deployment, probably with a slant toward integrating with ColdFusion. But anyone who is interested in learning more about Data Services, BlazeDS and how they can leverage the power of these technologies in to their web applications using Flex should get a lot out of it.
Tuesday, May 06, 2008
Conflict bug in ColdFusion Extensions for Eclipse
One of the great features of these extensions is the ability to use the RDS Dataview window to generate CFCs and Actionscript classes from your database tables, saving you lots of tedious typing. To do this go to Window -> Show View -> Other... -> ColdFusion -> RDS Dataview. This will open the panel that allows you to browse your data sources via RDS. Right click on the panel and select "RDS Configuation" to open the settings dialog and configure your RDS server. For instance I have my local CF server configured as:
Description: localhost
Host Name: 127.0.0.1
Port Number: 8500
I am using the built in web server (port 8500) and the stand alone configuration, so there is no context root. You must have RDS turned on in your installation (see technote here for details) .
Once you have the panel open and have access to your ColdFusion data sources, you can open up a DSN, open the "Tables" folder and right-click on a table name. You can show the contents of the table or open up the Query Viewer (on Windows) and run an arbitrary query. At the bottom of the context menu you should see "ColdFusion Wizards". Select that and the sub menu is "Create CFC".
This gives you a dialog that allows you to create CFCs that correspond to this table in your database. Not only that, it will create either "Active Record" or "Bean/DAO" style CFCs that know how to create, read, update and delete (CRUD) themselves! This is pretty cool in an of itself, but if you are using LCDS to write a Data Management application, this wizard can also write the Assembler CFC (the component that has the required methods for LCDS) for you. This selection is named "LiveCycle Data Services Assembler CFC's". It will generate the Bean CFC (representing 1 row of data), the Data Access Object (DAO) CFC, which has read/write/update/delete methods and the Assembler CFC itself, which uses the Bean and the DAO CFCs to do its work.
Neat, huh?
You can even create the Actionscript class that is used in the Flex application to represent your data when it gets to the client.
This code will work right out of the gate if you have a simple single table application. You can use it as a jump start for more complex applications that have multiple assemblers and a more complex data dependencies.
Unfortunately, there is a bug in the generated code when it comes to conflict detection. As part of the "sync" method, there are private functions which perform the create, update and delete operations (named doCreate, doUpdate and doDelete respectively). If you examine the doUpdate and doDelete methods, you will see a cfcatch clause with type="conflict". This calls the ChangeObject's conflict() function. This function takes as its argument the server's version of the object we are trying to update or delete, so the client application will have all three versions of the data - old, new and the server version:
<!--- If there was a conflict, mark the change object.
Include the current version of the record --->
<cfcatch type="conflict">
<cfset variables.dao.read(id=new.getARTISTID()))>
</cfcatch>
The problem is that the code is using the DAO read() method to get the server's version of the object. If you take a look at the DAO code, the return type is defined to be this:
<cffunction name="read" output="false" access="public" returntype="src.com.ARTISTS[]">
This is an array, which is not what we want to put in the ChangeObject for LCDS. What we really want to put in this object is a single record and to do this, we have to unwrap the array returned by read():
<cfcatch type="conflict">
<cfset readresult = variables.dao.read(id=new.getARTISTID())>
<cfset serverversion = readResult[1]>
<cfset co.conflict(serverVersion)>
</cfcatch>
You may want to include some error checking code in here to verify that the call to read() has returned exactly one result (it should as in the case ARTISTID is the primary key in the table). You also may want to "var" the readResult and serverVersion variables at the top of the function to keep them local (yes, this is annoying, yes we need to fix that).
An alternative approach is to invoke the get() method on the Assembler itself, which will take care of unwrapping the array and throwing errors if there is a problem. In order to do this you would need to create a Struct that contained "ARTISTID" as the get() function takes a map of name/value pairs to support the possibility of multiple primary keys. So this fix would look like this:
<cfcatch type="conflict">
<cfset uid = {ARTISTID=new.getARTISTID()}>
<cfset serverVersion = read(uid)>
<cfset co.conflict(serverVersion)>
</cfcatch>
Notice the use of the new ColdFusion 8 structure initialization!
I can't believe we (specifically I) didn't notice this problem before, but as it turns out my conflict functions did not make use of the server version of the records in all of the applications I have written so I (and I guess our QA folks) didn't notice the problem until just recently. On the bright side, this should be an easy thing to fix (and hopefully we will fix it in future releases of the Eclipse extensions) and gives me an opportunity to talk about this really helpful feature of the Extensions.
Tuesday, April 08, 2008
Array types in ColdFusion web services
Question - How do I create a function that has an array of custom made objects as an argument or return value?
First, let me recommend reading the ColdFusion 8 Developers guide chapter on web services (http://livedocs.adobe.com/coldfusion/8/htmldocs/webservices_01.html). I would also recommend reading Ben Forta's (et al) book, the Web Application Construction Kit Volume 3, chapter 68 – Creating and Consuming Web Services.
The first piece of information to know is that the CFML complex types, such as they are, might not the best things to use when creating a Web Service. Lets take Struct's for example. When you define an argument to a function as a struct, the XML Schema that is emitted for the WSDL defines an object that has the "any" type as both the key and the value. But this doesn't give the consumer of the web service much information - are there strings or complex types as the key? What kind of values should there be? Should there be various different things contained in the structure?
A better way to create this service is by defining exactly what kind of things you expect or return. To do this you would create a ColdFusion Component (CFC) that used the (mostly useless except for web services) cfproperty tag to describe the structure. Lets say our structure contained a just string pairs. Here is how you would define that:
<cfcomponent>
<cfproperty name="key" type="string">
<cfproperty name="value" type="string">
</cfcomponent>
Now our XML Schema in the WSDL would define a complexType that has two element in it at key (of type string) and value (also of type string). This has the advantage of clarity and also is potnetially much more interoperabile.
But the original question was about arrays. Lets get back to that.
If you want to publish a web service using CFC's then you would define the cffunction that takes an array as an argument or as a returnType. You can define this array to be of a particular CFC type that you have defined using the cfproperty tag as we did above. The example in the Forta book (Volume 3, page 298, listing 68.11 and 68.12) also shows this. The missing piece is how to specify that the argument is an array.
<cffunction name="GetCreditRating" returntype="string" output="no" access="remote">
<cfargument name="person" type="CreditPerson[]" required="yes">
Notice the "[]" after the name of the component. This will indicate that the WSDL should define the CreditPerson complexType and that the argument to the function should be 1 or more (MaxOccurs="unbounded") of these complex type elements i.e. an array. You can use this with types defined by CFCs or even for simple types (e.g. string[]). This syntax is valid in both the returnType attribute and the type attribute of cfargument.
This information is in the ColdFusion 8 documentation at
http://livedocs.adobe.com/coldfusion/8/htmldocs/webservices_20.html
The follow up to this is if you want to consume a web service (such as the one above) you would define an array in CFML and put in it Structs that correspond to the complex type in the WSDL. Here is an example again based on the GetCreditRating that uses the new CF8 syntax for creating stuctures:
<cfscript>
arg = ArrayNew(1);
s1 = { FirstName=Tom, Lastname=Jordahl, …};
s2 = { FirstName=Ben, Lastname=Forta, …};
…
arg[1] = s1;
arg[2] = s2;
</cfscript>
Then you would pass the “arg” array as the parameter to the invocation of the web service.
I hope this fleshes out a little but about array types for ColdFusion web services.
Monday, April 07, 2008
ColdFusion 8 Update 1 released
What does is have in it? Quoting from the from FAQ:
ColdFusion 8 Update 1 provides full 64-bit support for several new platforms including versions of Windows, Mac OS X, and Linux, in addition to Solaris (which was added with the initial ColdFusion 8 release). We have also improved functionality in several areas including AJAX functions, CFPDF, and CFIMAGE. We have also updated several software libraries.
Full details on these and other enhancements/fixes are available in the Update Release Notes.
Short answer: Lots of goodness.
To answer one question I have already gotten, there is NO update to the LiveCycle Data Services 2.5.1 bundled in with ColdFusion 8. Nor does the update contain BlazeDS. There are instructions that come with BlazeDS on how to integrate it with ColdFusion (see the resources/ColdFusion directory in the BlazeDS distribution).
Wednesday, April 02, 2008
CFUnited for 2008
I always enjoy attending CFUnited. It is being held at a new location this year. There probably isn't going to be a Dave & Busters handy to play games at, which is was always a highlight. But the crowd is always good and I really enjoy the sessions. Should be a good time, see you there!
Monday, March 24, 2008
Adobe Online Developer Week - March 24-28
The lineup seems pretty good and you can pick and choose which sessions you want to attend. Check out more details at http://adobe.com/go/2008_developer_week
Friday, March 07, 2008
ColdFusion 8 wins Dr. Dobb's Jolt Award!
Sales are great, awards are being won and ColdFusion continues to rock the web development world!
Thursday, February 28, 2008
Using ColdFusion with SQL Server XML/SOAP Endpoints
The interesting thing is that he did not use the HTTPClient library (although using this should have allowed the Digest and NTLM authentication to work since the library supports it) because the HTTPSender class (see source here) does in fact support HTTP 1.1, just not by default. The trick is to set the right property on the web service stub so that the Axis engine does the right thing.
The other note to John's post is that you can use cfinvoke if you want to, but you just have to use cfobject/CreateObject to create the web service "stub" first, then pass that in as the webservice attribute of cfinvoke.
<cfset ws = CreateObject("webservice", "https://webservice.yourcompany.com/sql/whatever_endpoint?wsdl")>
<cfset ws._setProperty("axis.transport.version", "1.1")>
<cfinvoke webservice="ws" method="JJData" returnvariable="result">
</cfinvoke>
John also notes that processing the result set and turning it in to a query is pretty slow. Not sure what is causing this other than there is a large amount of data getting processed.
Wednesday, February 27, 2008
BlazeDS presentation Feb 28, 2008
Check out the details here.
Here is the description:
Recently Adobe announced the release of BlazeDS which will make some key server technologies open source and free to use in any application. These technologies are critical to building great applications with Flex and AIR. Tom will talk about exactly what you get in BlazeDS and how it relates to LiveCycle Data Services and will detail some of the reasons why you might want to use these server technologies. He will also explain how ColdFusion developers can take advantage of BlazeDS in their applications.
Wednesday, February 20, 2008
Directory Watcher talk by Dave Ferguson
Anyway, if you do watch Dave's talk, there are a few questions and quirks he mentions that I wanted to try and clear up.
First, note that the Java source for this gateway is shipped with every copy of CF in
Dave notes that the "Out"count in the CF administrator is always 0 for this gateway. This is the number of messages/events that the gateway has sent, well, out. For instance how many times SendGatewayMessage() was called for this gateway. Since the Directory Watcher doesn't do outgoing messages (see line 187), this count will of course always be zero.
Dave mentioned that when he returns something from (in his case) one of the onAdd, onChange or onDelete functions, he gets a meaningless error in the logs. I filed a bug on this for Dave (#71311). The problem is that as a convenience the gateway system will take the return value of an event processing CFC function and send that "out" to the gateway. For instance if you receive an SMS or IM message to one of those gateways, you can return a Struct with a reply message from the onIncomingMessage function and the system will (in effect) call SendGatewayMessage() on that gateway with that struct. Since he was returning a simple value (i.e. "1") CF tries to make that a Struct, fails and reports the strange error. It *should* ignore that return value and log a warning (in the eventgateway.log) that it is doing this.
Dave was asked about timeouts for the CFC processing functions. Event Gateway CFC function calls are subject to the same timeout value as ColdFusion requests. A gateway can override this value (in the Java code) by calling CFEvent.setCfcTimeOut(). Otherwise the timeout is the same as for "regular" CFML page requests.
There was a question about file renames and what the Directory Watcher does when it sees this. The watcher does in fact report an ADD and a DELETE event when you change the name of a file. This is by design and I verified this on ColdFusion 7 and 8.
Hope this info helps those looking for more information about this gateway.
30 On Air launches
Why AIR? Why Flash Player? Why Flash? Why Flex? Why Coldfusion? Why AJAX? Why LiveCycle? Why Dreamweaver?
You can upload your own video also, just tag it on YouTube with the "30onair" tag. I like the rocking guitar solo video, but maybe I have been playing too much Guitar hero.