ColdFusion 10 websocket security issue fixed

Updater 11 for ColdFusion 10 has been released today. This updater fixes a number of issues. You can read all about the fixes here, ColdFusion 10 Update 11.

Most importantly, is the fix that will prevent websockets from invoking non-remote methods in components.

There is also a hotfix release for ColdFusion 9 to correct a security issue.

http://www.adobe.com/support/security/bulletins/apsb13-19.html

If you are running ColdFusion 9 or 10 I strongly suggest that you apply this patch.

Till next time,

--Dave

Combining query results using QofQ

I have a process that processes an xml document. For each node in the xml a stored proc is called to update data. What I wanted to do was combine all the results into a single query return. I did some digging and found an old blog post http://www.bennadel.com/blog/114-ColdFusion-QueryAppend-qOne-qTwo-.htm - Ben Nadel that showed how to do it. However, that post was a bit old so I figured there must be a better way by now.

I asked a friend and he suggested to try using query of queries with a union to combine them. This seemed intriguing but there was an issue with this. The code I was working with was all in cfscript. I remembered that dealing with queries of queries in cfscript is, well, challenging. I dug up a ColdFusion Cookbook entry http://cookbooks.adobe.com/post_Query_of_Query_with_CFSCRIPT-16492.html on using QofQ in cfscript.

Using this I was able to fashion together a function to combine two queries into one. I am not sure how will this would work if the queries had different columns. Now, instead of dealing with about 100 independent queries, I can deal with just one.

view plain print about
1private any function combineQuery(qA, qB)
2 {
3 var qry1Result = arguments.qA;
4 var qry2Result = arguments.qB;
5 var qoqResult = '';
6        
7 // create new query object
8 var qoq = new Query();
9
10 // set attribute of new query object to be a query result with arbortrary name
11
12 qoq.setAttributes(QoQsrcTableA = qry1Result);
13 qoq.setAttributes(QoQsrcTableB = qry2Result);
14
15 // use previously set attribute as table name for QoQ andset dbtype = query
16 qoqResult = qoq.execute(sql="select * from QoQsrcTableA union select * from QoQsrcTableB", dbtype="query");
17
18 // return result
19 return qoqResult.getResult();
20
21 }
22    
23}

Till next time...

--Dave

ColdFusion Platform Survey

Take a quick moment out of your day and fill out this survey. Let Adobe know what production platforms you are running ColdFusion on.

https://www.surveymonkey.com/s/HMQG62Y

Till next time...

--Dave

ColdFusion Developer Week Recordings

So, you missed one of the ColdFusion Developer week presentations? Fear not... all the sessions were recorded. You can view them all here... http://www.adobe.com/devnet/coldfusion/events.html.

Most, if not all, of the presenters have posted their slides and demo code as well. Unfortunately, the Adobe site doesn't list those. You might want to check the respective presenters blogs or use Google to find them.

till next time,

--Dave

ORM Event Handling

This is probably not life altering news for most. However, after spending a couple hours tracking down how to do something I figured I would blog it. What I was trying to do was setup an preupdate and preinsert events on a specific entity. I have previously setup global events so I figured this would be a slam dunk.

But first, what are event handlers and what can you do with them? Well, think of them as database triggers. They could be used to log record updates, or even data loads for that matter. You can find a full list of them here:

http://help.adobe.com/en_US/ColdFusion/9.0/Developing/WSDD377092-70D9-4b11-A19C-505F59EE31D1.html

To use event handlers you first need to enable them in your application. This is accomplished by updating the orm settings in your application.cfc file. The eventhandler setting is required if you are using global events.

view plain print about
1this.ormsettings = {
2eventhandling= "true",
3eventhandler - "your.event.cfc"
4}

As an example, lets say we want to update an created date any time a record is inserted. Instead of going to all your code where the entity is created it makes much more sense to add an event handler.

view plain print about
1public void function preInsert(any entity){
2        var thisTable = getMetadata(arguments.entity).table;
3        if (thisTable == 'myTable'){
4            entity.setCreateDate(now());
5        }
6        
7    }

In the code example above we run a preInsert event. The code checks for the table being inserted into and if it is the correct table, the create date is set.

This works just fine but it probably not a good candidate for a global event. This is more something that should be on the specific entity. This can be done by adding that same function, with some slight modifications, to the persistant cfc for the table.

view plain print about
1public void function preInsert(){
2        this.setCreateDate(now());
3    }

This is the part that tripped me up. The function is about the same with the exception of the arguments. Notice that for the entity specific function there are none. Also, to set the create date we use the "this" scope. The documentation from Adobe on event handlers gives poor, or non-existant examples of event handlers. So, trial and error, and google helped me figure this out.

So there you have it, when dealing with global events you need to reference the arguments scope when dealing with the entity. But, when dealing with a specific entity the "this" scope is utilized.

Update:

The great John Whish pointed out a couple other ways to accomplish this as well. Apparently the "this" scope is not necessary. You can also sit it without using the set method. I tried both of these and they worked just fine.

view plain print about
1public void function preInsert(){
2        setCreateDate(now());
3    }
4
5    public void function preInsert(){
6        variables.CreateDate = now();
7    }

Till next time,

--Dave

ColdFusion 10 and Builder 2.01 Public Beta

Yea.. title just about covers it. So... Go get it and check out all the new hotness. There are many new things to talk about and use. Here is a short list of the features that interest me the most.

  • Websockets
  • Switch from jRun to Tomcat
  • Rest web services
  • Security enhancements
  • Charting
  • Scheduler
  • Hotfix updater

http://labs.adobe.com/technologies/coldfusion10/

Go get it for your self. Come up with your own list. Build something awesome.

Till next time...

--Dave

Its JSON, I swear

I am mostly bogging this so I don't forget this stupid mistake I made. I am using jQuery to make an ajax call to a CFC. The CFC returns json data that I am using to populate a form.

The call worked just find and data was returned. However, for some reason, in JavaScript the JSON data was not being treated as JSON. I spent ages searching for solutions and doing trial and error.

This is the JavaScript call I was making:

view plain print about
1function getDetail(item){
2 $.ajax({
3 url: '/components/packages.cfc?method=getPackageByID',
4 cache: false,
5 type: "get",
6 data: {
7 packageid: item
8 },
9 success: function(data){
10 console.log(data.DATA);
11 }
12 });
13 }

This is the CFC that returned JSON.

view plain print about
1<cffunction name="getPackageByID" access="remote" returnformat="JSON" >
2        <cfargument name="packageid" required="true" type="numeric" >
3        
4        <cfset var getdata = "">
5        
6        <cfquery name="getdata" datasource="#request.dsn#" >
7            select * from package
8            where packageid = <cfqueryparam value="#arguments.packageid#" cfsqltype="cf_sql_integer" >
9        </cfquery>
10    
11        <cfreturn getData>
12    </cffunction>

Knowing what I know (which I started to question) I should have been able to use "data.DATA" to get to the JSON data in the result. However, when I output it to the console I would get "undefined". I also tried, among other things, "data[0]" which got me "{".

The lovely kicker was that the Chrome debugger network tab showed the call return as JSON.

I continued to try anything I could think of to get JavaScript to understand the return as JSON. All I ended up doing was finding a ton of ways to not process JSON.

Finally, I figured something out. It finally dawned on me that JavaScript was processing the data as a string not JSON. The "Intelligent Guess" feature of $.ajax() in jQuery was apparently failing to see the data as JSON.

I then added " dataType: 'json' " to the ajax call. Once I did that everything started working as expected.

If there is a moral here I would say it is to not expect data types to be auto assigned correctly. If you can, define what the data types will be. this will save you pain and anguish later.

Till next time...

--Dave

ColdFusion Developer Week

Time to get your learning on.. for FREE. Check out Adobe ColdFusion Developer Week for a weeks full of great sessions for the new and old alike.

I am especially excited about this as I am one of the speakers. I will be talking about ColdFusion and Mobile.

Check it out and sign up. http://www.adobe.com/cfusion/event/index.cfm?event=detail&id=1489920&loc=en_us

Till next time,

--Dave

CFLocation and hash based navigation

I was working to correct a bug in BlogCFC mobile. The bug happened when a user clicked on a link in an email notification they would get redirected to a blank screen. This would only happen when clicking on a link that was to a specific comment. My initial thought that the redirect link was being generated wrong because of the hash. This was not true, it was generated correctly.

For example, if this was the link to the post

http://blog.dkferguson.com/index.cfm/2011/6/3/BlogCFC-renderer-article

it would be redirected to the following if a user was on a mobile device

http://blog.dkferguson.com/mobile/index.cfm#./postDetail.cfm?post=5E2708D6-A21B-65AC-2232595C693D293D

This worked just fine. User would be taken to the correct post as expected. However, if the link was to a comment it would look like this:

http://blog.dkferguson.com/index.cfm/2011/4/5/jQuery-Mobile-Feed-Viewer#c843D1743-0227-884E-B34C31C282C6B66F

Then redirect to this:

http://blog.dkferguson.com/mobile/#postDetail.cfm?post=82608F56-B34A-2BEB-ECBC47375279E7E2#c843D1743-0227-884E-B34C31C282C6B66F

This redirect is where it was broke. First off, as you can see, there are two hash tags in the url. Secondly, nowhere in the redirect code is there anything to add the original hash to the comment. I know that because of how the mobile version works; linking directly to the comment won't work.

view plain print about
1<cfif isClientMobile()>
2    <!---Mobile Redirect--->
3    <cfset urlVars= listFirst(reReplaceNoCase(trim(cgi.path_info), '.+\.cfm/? *', ''), "##")>
4    
5    <cfif listlen(urlVars, '/') LTE 1> <!---NOT AN SES URL--->
6        <cfset urlVars = ''>
7    </cfif>    
8    <cfset path = cgi.http_host & ListDeleteAt(cgi.script_name, listLen(cgi.script_name, "/"), "/")>
9    <cfif NOT right(path, 6) EQ "mobile">
10        <cflocation url="http://#path#/mobile/index.cfm#urlVars#" addToken="false">
11    </cfif>
12</cfif>

I was using cflocation to perform the redirect so the extra hash puzzled me even further. I would not have expected the original hash to be there. After a ton of digging, I stumbled across something. The hash was being maintained and added to the redirect by the browser. A quick search on the net produced a few articles that discussed this very issue. The hash is never passed to the server. So, looking at the cgi scope or any other scope would never produce the hash or even elude to the fact that there was one there.

Now, after reading all this I understood what was happening. The extra hash was tripping up jQuery Mobile. It was trying to load a non-existent page. So, what I had to do was remove the hash to the comment from the url.

I tried a bunch of different things. But, as long as I was using cflocation I would never get around it. I had to do the redirect client side. Normally, I am not opposed to this. However, mobile is different. I now have to rely on a mobile browser. Something that can be unreliable for many reasons.

I first tried to do the redirect with JavaScript. This worked just fine. I just did a window.location and the user was redirected to mobile and the original hash was gone. I also tried doing with a meta tag. This also produced the same result. Now, to choose what to do.

In the end I went with the meta tag. Mostly because it ended up to be less data transfer and faster. I also didn't have to worry about the client processing JavaScript for the redirect.

So, what did I learn? Hash navigation is appended to a cflocation by the browser if relocating to the same domain. A relocation to another domain won't append the hash to the url. A client location change via JS or meta tag will remove the hash.

I hope you learned something from this as well.

Till next time...

--Dave

Hashing files and Java heap space

So, for those that did not know. The hash() function in ColdFusion can do a cool trick. It can generate a md5 hash of a file. Why would you want to do this? Well, if you want to compare 2 files at the byte level a hash is the best way. IF the files are identical byte for byte the hash will be the same. if one byte in the file changed the hash changes. The change could be something as simple as changing a file attribute or an update to its last modified date.

Here is the code to do it. It is very simple.

view plain print about
1<cfset fileHash = hash(FileRead(myfullFilePath), 'MD5')>
This code would result in something that looks like this: 6168f305888c3d795e67c6de17bf8a21

This works perfectly in about 90+% of all cases. However, in what I was doing it worked in about 5% of all cases. The simple fact is that I was trying to hash files that were in the 250mb+ range. When doing this my server would start generating this lovely error:

view plain print about
1"Error","Thread-26","12/29/09","15:27:48",,"Error invoking CFC for gateway ProcessFile: Java heap space."
2java.lang.OutOfMemoryError: Java heap space
3    at java.util.Arrays.copyOfRange(Arrays.java:3209)
4    at java.lang.String.<init>(String.java:216)
5    at java.lang.StringBuffer.toString(StringBuffer.java:585)
6    at coldfusion.tagext.io.FileUtils.readFile(FileUtils.java:174)

I have not been able to determine the exact file size that causes this. I have seen it with files as small at 50mb. But, I do see it very regularly with files over 100mb. I have also not found a way around it yet. I currently just wrap the code inside a cftry to suppress the error.

Till next time...

--Dave

More Entries