Thursday, March 9, 2017

ColdFusion 2016 Broke My API (And How I Fixed It)

While on ColdFusion 9, I built a REST API for our an application at work. Everything was working fine until we upgraded to ColdFusion 2016. When I went to test the API, I got an HTTP 500 error. The Apache log gave me no clues to what was happening, so my next stop was the ColdFusion logs. In the exception log, I found this:
Error","ajp-nio-8015-exec-9","03/09/17","07:22:12",,"Application  could not be found. The specific sequence of files included or processed is: '''' "
javax.servlet.ServletException: Application  could not be found.
        at coldfusion.rest.servlet.CFRestServlet.invoke(CFRestServlet.java:512)
        at coldfusion.rest.servlet.RestFilter.invoke(RestFilter.java:60)
        at coldfusion.filter.ExceptionFilter.invoke(ExceptionFilter.java:94)
        ...
A quick Google search did not reveal much. However, the fact that it was calling the CFResetServlet gave me an idea. What if the /api directory now had special meaning? So I renamed the /api directory to /API-TEST and sure enough my code was working again.
I found the solution in the web.xml file:
    <servlet-mapping id="coldfusion_mapping_16">
        <servlet-name>CFRestServlet</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>
This mapping is forcing everything in the /api directory to be processed by the ColdFusion REST service. To fix this, I simply commented out this section of the configuration and restarted the ColdFusion service.
    <--
    <servlet-mapping id="coldfusion_mapping_16">
        <servlet-name>CFRestServlet</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>
    -->

Friday, October 28, 2016

Chicken and Sausage Gumbo Recipe

Today I take a break from my usual tech blog posts to post a recipe. However, this is still tech-related as I am exploring the recipe schema to post this. If my code works, then the recipe should be formatted better for Pinterest, search engines, and recipe applications.

Chicken and Sausage Gumbo

Ingredients

  • 1/2 Cup Cooking Oil
  • 1/2 Cup Flour
  • 1 White Onion
  • 1 Bell Pepper
  • 4 Stalks Celery
  • 2 32-oz Cartons of Chicken Stock
  • Tony Chachere's Creole Seasoning (To taste)
  • 2 Bay Leaves
  • 1 Package Chicken Breasts
  • 1 Package Andouille Sausage*
  • Cooked Rice

Cook Time

Instructions

  1. Cut up chicken breasts into chunks and place into a bowl. Season with creole season, cover bowl, and put in refrigerator.
  2. Chop onion, bell pepper, and celery. Set aside.
  3. In a heavy 4-quart pot or Dutch oven, warm cooking oil over medium to medium-low heat.
  4. Add flour to oil and stir continuously until roux becomes a dark brown color. Do not burn. If black flecks appears, you will need to start over. It will take approximately 30 minutes to get a nice brown color.
  5. Add vegetables and cook in roux until onions begin to wilt.
  6. While stirring the mixture, slowy pour in chicken stock. Bring to a boil and then reduce to a simmer.
  7. Stir in seasoning and bay leaves. Adjust to taste.
  8. Carefully add meat to gumbo.
  9. Cut up sausage and add to gumbo
  10. Simmer gumbo for 1-2 hours or until chicken is tender
  11. Serve over cooked rice. Goes well with French bread and deviled eggs

* I prefer a real smoky sausage from a local smokehouse for the rich flavor but I usually do not have time to get that. So I like to buy Holmes Andouille sausage at the grocery store because it has a nice flavor and it is not greasy at all.

Thursday, October 6, 2016

Format JSON String in ColdFusion

I am sure this has probably already been done but I couldn't find it quickly with a Google search. So I wrote my own. This function takes a JSON string and indents it to make it more readable.
<cffunction name="indentJSON" hint="Indents JSON to make it more readable">
    <cfargument name="JSONString" default="" hint="JSON string to be formatted">
    <cfargument name="indentCharacters" default="#Chr(9)#" hint="Character(s) to use for indention">

    <cfset local.inQuotes = false>
    <cfset local.indent = 0>
    <cfset local.returnString = "">
    <cfset local.stringLength = Len(arguments.JSONString)>
    <cfloop index="i" from="1" to="#local.stringLength#">
        <cfset local.currChar = Mid(arguments.JSONString, i, 1)>
        <cfif i lt local.stringLength - 1>
            <cfset local.nextChar = Mid(arguments.JSONString, i + 1, 1)>
        <cfelse>
            <cfset local.nextChar = "">
        </cfif>
        <cfif local.currChar eq '"'>
            <cfset local.inQuotes = !local.inQuotes>
        </cfif>
        <cfif local.inQuotes>
            <cfset local.returnString = local.returnString & local.currChar>
        <cfelse>
            <cfswitch expression="#local.currChar#">
                <cfcase value="{">
                    <cfset local.indent = local.indent + 1>
                    <cfset local.returnString = local.returnString & "{" & Chr(10) & RepeatString(arguments.indentCharacters, local.indent)>
                </cfcase>
                <cfcase value="}">
                    <cfset local.indent = local.indent - 1>
                    <cfset local.returnString = local.returnString & Chr(10) & RepeatString(arguments.indentCharacters, local.indent) & "}">
                    <cfif local.nextChar neq ",">
                        <cfset local.returnString = local.returnString & Chr(10)>
                    </cfif>
                </cfcase>
                <cfcase value="," delimiters="Chr(0)">
                    <cfset local.returnString = local.returnString & "," & Chr(10) & RepeatString(arguments.indentCharacters, local.indent)>
                </cfcase>
                <cfcase value=":">
                    <cfif local.nextChar neq " ">
                        <cfset local.returnString = local.returnString & ": ">
                    </cfif>
                </cfcase>
                <cfdefaultcase>
                    <cfset local.returnString = local.returnString & local.currChar>
                </cfdefaultcase>
            </cfswitch>
        </cfif>
    </cfloop>

    <cfreturn trim(local.returnString)>
</cffunction>
And here's an example:
<cfset variables.testObject = {}>
<cfset variables.testObject.name.first = "Chad">
<cfset variables.testObject.name.last = "Armond">
<cfset variables.testObject.title = "Software Developer">

<cfset variables.testString = SerializeJSON(variables.testObject)>

<cfoutput>
    <h1>With Tabs (Default)</h1>
    <cfset variables.json1 = indentJSON(variables.testString)>
    <pre>#variables.json1#</pre>

    <h1>With Spaces</h1>
    <cfset variables.json2 = indentJSON(variables.testString, "    ")>
    <pre>#variables.json2#</pre>
</cfoutput>
And the results:

With Tabs (Default)

{
 "NAME": {
  "LAST": "Armond",
  "FIRST": "Chad"
 },
 "TITLE": "Software Developer"
}

With Spaces

{
    "NAME": {
        "LAST": "Armond",
        "FIRST": "Chad"
    },
    "TITLE": "Software Developer"
}

Wednesday, October 5, 2016

Converting Perl Arguments from Named Arguments to Positional Arguments While Maintaining Legacy Code

I think that's the longest blog title I've ever written. In case it's not clear, here is what is happening.

I used to use only positional arguments in all of my Perl functions simply because I didn't know any better. There are problems with that approach as this article points out. With any new development, I am solely using named arguments but what about legacy code? I wondered if I could change a method to accept named arguments but still use the same positional arguments so that I wouldn't have to update my legacy code base. Here's what I came up with:
if (ref(@_[0]) eq "HASH") {
    my %args = %{@_[0]};
    foreach my $arg (keys %args) {
        eval("\$$arg = \$args{\$arg}");
    }
}
What this code snippet does is look at the first argument in the list to determine its type. If it's a HASH, then we know the arguments are being passed as named arguments. So we convert them using the eval function to look just like the old positional arguments.

So let's say we have this legacy Perl function:
sub test {
    my ($arg1, $arg2, $arg3) = @_;
    
    print "\$arg1 = $arg1\n";
    print "\$arg2 = $arg2\n";
    print "\$arg3 = $arg3\n";
}
To convert it, we just add the new code snippet in. Here's a test script to see it in action.
#!/usr/bin/perl

use strict;

&test("abc", "123", "xyz");

&test({
    arg1 => 'abc',
    arg2 => '123',
    arg3 => 'xyz'
});

sub test {
    my ($arg1, $arg2, $arg3) = @_;
    
    if (ref(@_[0]) eq "HASH") {
        my %args = %{@_[0]};
        foreach my $arg (keys %args) {
            eval("\$$arg = \$args{\$arg}");
        }
    }
    
    print "\$arg1 = $arg1\n";
    print "\$arg2 = $arg2\n";
    print "\$arg3 = $arg3\n";
}

Wednesday, June 29, 2016

Including ColdFusion Content in Perl CGI Script

I have a site that mostly consists of ColdFusion pages. Occasionally I will use a Perl CGI script when the need arises. For example, long running reports or scripts that need to run shell commands are better suited for Perl than ColdFusion. On a side note, if you are running ColdFusion on a UNIX/Solaris platform like I am, you should avoid CFEXECUTE tags at all costs.

Whenever I create a Perl script on my site, I still want it to have the look and feel of the rest of my ColdFusion pages. I have a standard header that is included on every page of my site. Within that header is a menu that changes depending on which role(s) the user is assigned. To include the header on my Perl pages, I just use wget to retrieve the header file from my site, then display the HTML. It is something similar to this:
print &getHTMLHeader();

sub getHTMLHeader() {
    return `wget -O - http://mysite/myheader.cfm`;
}

The problem is that my ColdFusion session is not passed. Therefore, the menu does not display what a logged in user should see. I first thought I could just pass my cookies to the wget command:
sub getHTMLHeader() {
    open OUT, ">cookies.txt":
    print OUT $ENV{'HTTP_COOKIE'};
    close OUT;
    return `wget --load-cookies=cookies.txt -O - http://mysite/myheader.cfm`;
}

However, the ColdFusion session management is smart enough to recognize that something is not right about this session. That's because the IP address of the server and not the client is being passed to the page.

So here is the solution. After the HTML header is received, I append a little jQuery code to get the menu code and replace what is displayed:
sub getHTMLHeader() {
    my $html = `wget -O - http://mysite/myheader.cfm`;
    $html .= <<"    END";
        <script>
        \$.ajax({
            url: '/path/to/menu/menu.cfm',
            async: false,
            dataType: 'html',
            success: function (data, textStatus, jqXHR) {
                \$(".main-menu-content").html(data);
            }
        });
        </script>
    END
    return $html;
}

Saturday, January 16, 2016

Making Set Lists for Rehearsing

After a long break, I have begun playing bass guitar on a semi-regular basis at church again. Music has never come naturally to me, so I require a lot of practice. To help with that, I've learned of a few tools to make that easier. Here are some of those.

YouTube

I use YouTube in several ways for rehearsal. The first is making song lists so that I can familiarize myself with the songs that I will be playing. I won't go into the details of how to do that, but whether you use a computer or a mobile device, you can easily build a playlist of songs.

Sometimes the song leader will provide a MP3 rehearsal track. While this is useful in itself, I like to have all of my songs in a YouTube playlist so that I can listen through the entire set list in my car. It's also helpful when I'm practicing if I have a continuous playlist instead of jumping back and forth between YouTube and an MP3 player. So that brings me to my first tool: TunesToTube.

TunesToTube

TunesToTube allows you to upload an MP3 file to your YouTube account as video. You can either select a static image from your computer or let it generate a generic background for your video. Whenever I upload a video like this, I always set the privacy set in YouTube to private since this video is only for my rehearsal purposes.

YouTube MP3convert2mp3.next

Sometimes I need to go the opposite direction and take a song from YouTube and download it to my PC. For that I use YouTube MP3 convert2mp3.next. Simply paste in the URL of the YouTube video and it will generate an MP3 file for download.

Audacity

I love Audacity. It is "a free, open source, cross-platform software for recording and editing sounds". It is useful in many ways, but for this discussion, I want to talk about the transpose feature. Often the key of the song that the original artist recorded the song in is not the same key we will be performing in it. Sometimes I will practice the song in the key of the recording just to familiarize myself with the song. But I like to practice the song in the key that I will be playing it. That's where Audacity comes in.

To transpose a song in Audacity, first open the song using File -> Open.
(Keyboard shortcuts: Ctrl+O in Windows and ⌘+O on Mac)

Next select the entire wave form using Edit -> Select -> All. (Keyboard shortcuts: Ctrl+A in Windows and ⌘+A on Mac)

Then select Effect -> Change Pitch from the menu:


Then choose the key you are transposing from and the key you want to transpose to and click OK:


You can then save it as an MP3 file by going to File -> Export as MP3. (Note that before you do this for the first time, you will need to install an additional dependency called the "LAME MP3 Encoder". Instructions can be found here.

* Updated 5/20/17: YouTubeMP3 no longer works for me. convertmp3.net seems to work better anyway.

Thursday, August 27, 2015

Using jQuery and ColdFusion to Retrieve and Save a List of Files

The Issue: We are required to scan our Web sites at work to insure that they meet Section 508 guidelines for accessibility.

The Problem: The scanner that we are required to use does not recognize spider traps. Since the site I have to scan contains over 4 million records and thus over 4 million URLs, the scanner will take hours to complete a scan and then report the same issue 4 million times.

The Fix (First Iteration): When the site was smaller, I would just save an example of each page to a directory on the Web server, then scan that directory for issues. The problem with this approach was that 1) it required me to manually save these HTML files and 2) would require me to remember each file that needed to be checked.

The Fix (Second Iteration): I decided a better approach would be to create a list of all URLs that needed to be scanned. Then write a server-side script to retrieve each one of those pages using wget and save them to the server. But I ran into a problem. Authentication on this site is a single sign-on (SSO) application that works by redirecting the user to a log in page on a different server, then redirect back after the user has successfully logged in. Maybe that could be handled in the server side script, but I don't want to figure out the code to do that.

The Fix (Final Iteration): It then occurred to me that a better solution would be to retrieve these files through AJAX. I would require authentication for my main page and then the AJAX calls would include my credentials. Of course client-side JavaScript can't save files to the server, but I found a way around that. Here's an overview of the solution:

Diagram illustrating an overview of the process

My main ColdFusion page contains a list of relative URLs to test. Then it loops through those to generate AJAX requests. JavaScript then returns HTML data. I pass the file name and encoded HTML back to ColdFusion through a second AJAX call. Then that ColdFusion page saves the HTML to my specified data. Here's a snippet of the code:
<cfset variables.count = 0>
<cfloop array="#variables.pagesToCheck#" index="variables.url">
    <cfset variables.count = variables.count + 1>
    <script>
    $(function() {
        $("#current-file").text('Processing <cfoutput>#variables.url#</cfoutput>');
        $.ajax({
            url: '<cfoutput>#variables.url#</cfoutput>',
            async: false,
            dataType: 'html',
            error: function (jqXHR, textStatus, errorThrown) {
                $("#pbar").progressbar({value:<cfoutput>#variables.count#</cfoutput>});
                $('#file-list').append('<li style="font-weight: bold; color: red">Could not retrieve <cfoutput>#variables.url#</cfoutput>.  Error: ' + errorThrown + '</li>');
            },
            success: function (data, textStatus, jqXHR) {
                $.ajax({
                    async: false,
                    type: 'POST',
                    url: 'save_html.cfm',
                    dataType: 'json',
                    data: {
                        source: escape('<cfoutput>#variables.url#</cfoutput>'),
                        html: escape(data)
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        $("#pbar").progressbar({value:<cfoutput>#variables.count#</cfoutput>});
                        $('#file-list').append('<li>Could not process #variables.url#  Error: ' + errorThrown + '</li>');
                    },
                    success: function (data, textStatus, jqXHR) {
                        $("#pbar").progressbar({value:<cfoutput>#variables.count#</cfoutput>});
                        $('#file-list').append('<li>' + data.source + ' - ' + data.success + '</li>');
                    }
                });
            }
        });
    });
    </script>
</cfloop>
And then here is the source of the save_html.cfm page:
<cfset variables.file_name = ReReplace(form.source, "[^\w\_]", "-", "ALL")>
{
    "source": "<cfoutput>#form.source#</cfoutput>",
    "success":
<cftry>
    <cffile action="write" file="#application.webroot#/508/#variables.file_name#.html" output="#URLDecode(form.html)#">
        "Saved"
    <cfcatch type="any">
        "Failed to save"
    </cfcatch>
</cftry>
}
Now when I'm ready for 508 testing, I just run my page to create all of my HTML pages then set my scanner to my /508 directory.
 
Blogger Templates