Archive for the ‘Write that one down!’ Category

All your base are visible to us

August 9, 2011

Here’s a little safety switch for utility web pages. I have a quick-n-dirty, static html file I use simply for submitting a form to test certain features of our site. I typically use this only in my local development but occasionally in other environments. I set it by un-commenting a desired <base> element like so:

<head>
  <title>Post Test Form</title>
    <!--
    <base href="https://local.example.com/path/to/feature" />
    <base href="http://qa.example.com/path/to/feature" />
    <base href="http://production.example.com/path/to/feature" />
    -->
    <base href="http://test.example.com/path/to/feature" />
    …

This typically works well for me, but I have at times changed the ‘active’ base element and forgotten. If only the base element were rendered somewhere in the browser. I could reveal the base element with CSS (display: block), but the base element has no content, only an href attribute.

Generated content to the rescue:

    /* first reveal the 'head', hide all the head's ancestor elements, un-hide the base element */
    head {display:block;}
    head * {display: none;}
    head base {display: block;}

    /* use a pseudo-class that enables generated content */
    head base:after {
        border: 1px solid ActiveBorder;
        background-color: ActiveCaption;
        color: CaptionText;
        display: block;
        /* display the value of the href attribute */
        content: attr(href);
        padding: 1ex;
        font-family: monospace;
    }

Now, the top of my static page has a block that displays the current ‘active’ base element. While I’m at it, how about one more little tweak to warn me when I’m in production?

    head base[href="http://production.example.com/path/to/feature"]:after {
        background-color: #FEE;
        color: #F00;
        font-weight: bold;
    }

Now, my view into my base element will show bold and red if I’ve un-commented the production base element.

Treating Customers Well

June 9, 2011

We send millions of emails every year trying to help people. To help us have unique conversations with all those people simultaneously, we rely on a vendor named Exact Target. Around here we tend to push the envelope, and with very few exceptions they’ve been able to keep pace with us every step of the way.

ExactTarget Care Package

Not long ago, we had a service interruption. We use their API to do a lot of different things, and several times over the period of a week, our API requests started failing. We quickly found and used a workaround to get by it the first few times, but when it kept happening we felt like we needed a deeper investigation. After some work, they found the issue on their end, they apologized profusely, and corrected it. Two days later, I arrive at my desk and find a package from them. It included a handwritten note, a $5 Starbucks gift card, and orange-flavored candy. (Everything they do is orange.)

I sent our contact an email thanking them for the gift and got this reply:

Tim, I’m glad you enjoyed the token of our consolation for the recent issues! Obviously candy oranges do not negate the product issues, but we hope it proves our appreciation for your understanding and solid partnership. We also appreciate the feedback and kind remarks – it always helps make a day! Please let us know how we can continue to help and support your team in the future. Best, Megan

Our team strives to super-serve people and we like to build relationships with vendors that do the same.  The way Exact Target handled this situation impressed me. It’s awesome to see things done at such a high level of excellence. I’m so impressed with it that I’m showing this care package to people around our office hoping that we can learn from the example. How does your company super-serve your customers?

What’s in YOUR work week?

April 6, 2011

“Time – got the time tick-tick-tickin’ in my head” – Joe Jackson

I need more time!

Let’s take a look at the average work week.  A handful of one hour meetings, maybe one large meeting, many smaller meetings.  By mid-week the calendar is almost full.  People are coming to your desk constantly asking for small adjustments/favors/tasks.  That time-saving-must-do project you had in mind, once again, received no time.  It’s the end of the week, you’re drained, and looking back you’re not sure what really happened.

You can read time management books, take a Covey course and recite all of the key phrases.  These are all good, and highly recommended.  But I’m going to fast forward to a different ending that’s not in the popular reading materials.  First let’s post a supposition:

Jim’s Axiom – Creativity does not exist without constraints.

You may be thinking “I can’t be creative WITH constraints!”  I respectfully say you are wrong.  Let’s go back to when you were younger.  You had a clean, unwrinkled, pristine sheet of paper (or an entire pad) and a pencil.  You looked down and… nothing.  “Mom, what should I draw?”

Did you see what just happened there?  Maybe you remember that, maybe you just heard your own child ask that very question.  There are no constraints.  You (or they) can draw anything, and you draw… nothing.  Where do you start?  What should you do?  If only you had a starting point you could go anywhere.  You need a constraint.

When the pressure is on, and you need to reach the goal line you do what it takes, you look for the opportunity, and you press forward and find a way.  Once there are constraints your creativity can take root and navigate through and bring you to success.

Jim’s Corollary – Creativity is the ability to solve a problem when there are constraints.

I need to find a way to the store and the main road is closed.  Creativity.
I need to buy groceries and I’m low on funds.  Creativity.
I need to get more done and I’m running out of time.  Creativity.

The Pony Express was infamous for delivering mail.  We could have grown that system and bought more horses.  When they weren’t fast enough we could have bred faster horses.  But the limitations were real, and no matter how much you did more of the same thing you couldn’t solve the pressing issue.  It doesn’t scale properly, and was not cost effective.  There had to be a better way.

This new way did not involve a horse.  It was the telegraph.  A completely different way of dealing with the issue.  Taking a broken system and spending more time doing it doesn’t get you the advance you’re really looking for.  The same is true during your work day.  If you take your current process and just add more time you do get more done.  But at what cost?  And what value are you really adding?

If you find yourself working 60+ hour weeks I’m going to challenge you.  Are you really only smart enough to just do more of the same to beat out your competition?  Is that how you sell yourself?  What happens when the next guy comes along and works 70 hours?  80?  They win?

But what about studies that state efficiency decreases as hours per week increases?  A quick search on Google yielded this: “The average efficiency for 50 hr, 60 hr, and 70 hr weeks was 0.92, 0.84, and 0.78, respectively.”  That means that at 70 hours per week you’re as efficient as actually working 55 hours.  You’ve heard the adage… why have you denied it?  Work smarter, not harder.

• Look at the work you’re doing and note the time spent.  What’s really eating your day?  What new process can you put in place to replace the old, inefficient model?  Focus on the important/non-urgent, and manage the important/urgent.  Avoid the non-important items!  Recommended reading:  First Things First by Steven Covey (Full disclosure: I’ve taken the Seven Habits course and love his style on time management, but I haven’t read this book yet.  Ironic I know.)

• The next step is to examine your processes.  Why and when you do what you do.  Keep your meetings focused and only invite those that should be there.  Set an agenda with a desired outcome, and once reached, end the meeting, even if it’s early!  Recommend reading: Death By Meeting by Patrick Lencioni.

• Are you trying to do it all on your own?  Who’s your model or mentor for that?  Look for strategic partnerships to accomplish more than you could do on your own.  Recommend reading:  Mentored By A Millionaire by Steven Scott.

As a young boy I heard a wealthy and successful man say something that has stuck with me.   I believe I’m finally starting to grasp the full intent, and I’ll leave it with you now:

“If you can’t do it in 40 hours a week, you’re doing it wrong.”

Add Spring Security to ColdFusion

January 10, 2011

We are evaluating Spring Security as a part of our authentication and authorization platform. Since we have many ColdFusion apps, it was natural for us to see how it would work in tandem with ColdFusion. Fortunately, since ColdFusion is a Java web application, Spring Security fits right in.

In this post, I’ll show the basics of connecting Spring Security with ColdFusion. I’m not intending to teach how Spring Security works or other details of implementation. For more information, see the Spring Security Reference Documentation (also available as a PDF).

At a high level, you add Spring Security as a Servlet Filter that is setup to run before the ColdFusion servlet in the Java web application config file (web.xml). To make it as easy as possible to see how it works, I’ll show you how to to setup the Spring Security tutorial app. I’m starting with a new install of ColdFusion 9.0.1, installed as a J2EE app under Tomcat 6.0.29 (running on JDK 1.6.0.22, 64-bit on Windows XP Pro).

  1. Download Spring Security from here (using version 3.0.5, the latest as of this writing).
  2. Extract the zip file (spring-security-3.0.5.RELEASE.zip).
  3. Extract the tutorial WAR file located in
    spring-security-3.0.5.RELEASE/dist/spring-security-samples-tutorial-3.0.5.RELEASE.war
    to a local directory (I’ll call it <tutorial-war>).NOTE: If you’ve never run the Spring Security tutorial, you can just put the <tutorial-war> directory into the /webapps directory of your Tomcat app server and run it. Here is more info about Spring Security tutorial.
  4. Copy all of the .jar files from:
    <tutorial-war>/WEB-INF/lib
    into:
    <cf-app>/WEB-INF/lib
  5. Copy the file:
    <tutorial-war>/WEB-INF/classes/log4j.properties
    to:
    <cf-app>/WEB-INF/classes/log4j.properties
  6. Copy the file:
    <tutorial-war>/WEB-INF/applicationContext-security.xml
    to:
    <cf-app>/WEB-INF/applicationContext-security.xml
  7. Copy indicated lines from:
    <tutorial-war>/WEB-INF/web.xml
    to:
    <cf-app>/WEB-INF/web.xml 

    1. Under the line:
      <description>Adobe ColdFusion 9</description>
      (line 5 in my stock CF9 web.xml), insert the <context-param /> blocks from
      <tutorial-war>/WEB-INF/web.xml
      (NOTE: In the code below, I removed the first line in the first <context-param /> from the tutorial that said
      classpath:applicationContext-business.xml.):

      	<!-- INSERTED FOR SPRING SECURITY -->
      
          <!--
            - Location of the XML file that defines the root application context
            - Applied by ContextLoaderListener.
            -->
          <context-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>
      			<!-- REMOVED classpath:applicationContext-business.xml -->
                  /WEB-INF/applicationContext-security.xml
              </param-value>
          </context-param>
      
          <context-param>
              <param-name>log4jConfigLocation</param-name>
              <param-value>/WEB-INF/classes/log4j.properties</param-value>
          </context-param>
      
          <context-param>
              <param-name>webAppRootKey</param-name>
              <param-value>tutorial.root</param-value>
          </context-param>
      
      	<!-- END INSERT FOR SPRING SECURITY -->
      
    2. Under the many <context-param /> blocks and before the <filter /> blocks (around line 64 in my stock CF9 web.xml), insert:
      	<!-- INSERTED FOR SPRING SECURITY -->
      
          <filter>
              <filter-name>springSecurityFilterChain</filter-name>
              <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
          </filter>
      
          <filter-mapping>
            <filter-name>springSecurityFilterChain</filter-name>
            <url-pattern>/*</url-pattern>
          </filter-mapping>
      
      	<!-- END INSERT FOR SPRING SECURITY -->
      
    3. Under the <filter-mapping /> blocks and before the <listener /> blocks (around line 167 in my stock CF9 web.xml), insert:
      	<!-- INSERTED FOR SPRING SECURITY -->
      
          <listener>
              <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
          </listener>
      
          <listener>
            <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
          </listener>
      
          <listener>
              <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
          </listener>
      
      	<!-- END INSERT FOR SPRING SECURITY -->
      
  8. Now, we have to adjust ColdFusion’s logging configuration so that Spring Security’s configuration and libraries are favored.
    1. Rename the file:
      <cfapp>/WEB-INF/cfusion/lib/commons-logging-1.1.1.jar
      to:
      <cfapp>/WEB-INF/cfusion/lib/commons-logging-1.1.1.jar.bak
    2. Rename the file:
      <cfapp>/WEB-INF/cfusion/lib/commons-logging-api-1.1.1.jar
      to:
      <cfapp>/WEB-INF/cfusion/lib/commons-logging-api-1.1.1.jar.bak
    3. Rename the file:
      <cfapp>/WEB-INF/cfform/jars/commons-logging.jar
      to:
      <cfapp>/WEB-INF/cfform/jars/commons-logging.jar.bak
    4. Rename the file:
      <cfapp>/WEB-INF/cfform/jars/commons-logging.properties
      to:
      <cfapp>/WEB-INF/cfform/jars/commons-logging.properties.bak
    5. Create the file:
      <cfapp>/WEB-INF/classes/commons-logging.properties
      , and insert the following into it:

      	org.apache.commons.logging.LogFactory=org.apache.commons.logging.impl.SLF4JLogFactory
      	org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
      
  9. Extract the file security.tld from the META-INF directory of the file
    <cfapp>/WEB-INF/lib/spring-security-taglibs-3.0.5.RELEASE.jar
    and put it into the
    <cfapp>/WEB-INF/lib
  10. Edit the file:
    <cf-app>/WEB-INF/applicationContext-security.xml
    and comment out the <session-management /> block (around line 35), like so:

    	<!--
    	<session-management invalid-session-url="/timeout.jsp">
    		<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
    	</session-management>
    	-->
    
  11. That should be all of the server-side configuration you need to make Spring Security work with ColdFusion. When you startup your app server, you should see a lot of Spring Security related output in your console, ending with:

    Root WebApplicationContext: initialization completed in  ms

    Since you can use JSP Tag Libraries in ColdFusion templates or CFCs, you can import Spring Security’s taglib and then easily restrict access to certain content or code using Spring Security’s access expressions. Here’s an example CFM file.

    <cfimport taglib="/WEB-INF/lib/security.tld" prefix="security">
    <html>
    <head>
    	<title>Spring Security on ColdFusion tester</title>
    </head>
    <body>
    	<h1>Spring Security on ColdFusion Test Application</h1>
    	<cfinclude template="/include/nav.cfm" />
    
    	<security:authorize access="isAuthenticated()">
    	<p>You can see this because you are authenticated.</p>
    	</security:authorize>
    
    	<security:authorize access="hasRole('ROLE_SUPERVISOR')">
    	<p>You can see this because you have supervisor permission.</p>
    	</security:authorize>
    
    </body>
    </html>
    

    That’s just the beginning — but a pretty strong beginning that opens up a whole new world of enterprise-class security.

Code Wants to be Stoopid

October 27, 2010

Clever code never did no-one nay but no good.  Not know-how, not no-way.

Yeah, I’m not sure what I just said either.  I do know that “clever” and “slick” are words that I don’t care to hear about code, or a coding solution to a given problem.  Code needs to be simple.  How simple?  As simple as possible [but no simpler, yadda yadda yadda].  “But my code IS simple” you say.  I’m done.

Cool.

…just one more thing.  Do all your methods only do one, and only one, thing?  Yes, I said all.

Every method (or function) should accomplish only what it’s name says it does.  If you have a method “saveThing” it should only save that thing.  It should not check for something else, optionally set a flag, make any other call, then save that thing, and return the price of tea.  It should only save the thing.  This means you will have many very small methods.

At a high level this will lead to something really powerful:  Readable code.  You see, as your fine granularity builds upwards you are essentially utilizing the ‘composition design pattern’ in a micro-economy.

What are some signs that you may have code that can be refactored into it’s own method?

  • Length.  If a method is growing beyond 10 lines, take a close look and make sure it’s still doing only one job.  As methods grow they want to do more than they should.  You can’t convince me that 1,000 line method is as small as it can get.
  • Unused parameters or only using one or two variables off an object.  Too much data will lead to putting process in the wrong place.  If you don’t have the data you can’t do the wrong thing with it.  Don’t pass in a few extra objects just in case.  If you don’t need them pull them out.  Use static analysis to remove unused and dead parameters (and code blocks).  If you really only need to do work on a username… just pass in the string, and not the whole user object.
  • Comments.  If you feel the need to explain a section of code inline, it should be it’s own function with a well suited name.  You either use too many comments (rare, and not cool but that’s another topic) or you use one or two in confusing places.  That’s the tingly feeling you’re trying to learn that says “put this code in it’s own method”.

This is the path to better code.  Code that does what it says it does, nothing more and nothing less.  It sounds so simple, but is the essence of a truly powerful system.  Keep your code dumb, you’ll thank me for it.

P.S.  I know it’s spelled “stupid”.  😉

Automated testing and dynamic ID’s

May 5, 2010

First off, if you’ve never used the testing tools Selenium or WebTest, you should give them a try.  Each of these tools is used to generate and run scripted interaction with your Web site (i.e. Web app functional testing).

As I built a regression test for a bug I found, I needed to automate selecting an option from a drop-down. Usually the test recorders handle this for me, and I give it no thought.  The catch is, the ‘id’ of the select element could be different on any given test run.  What I really needed was to choose a select element based on its label rather than its ‘id.’

Consider this (dynamically generated) HTML fragment:

<label for="arbitraryElementId">Product</label>
<select name="productX_another_arbitrary_id" id="arbitraryElementId">
   <option>foo</option>
   <option>fizzle</option>
</select>

<label for="somethingElse">Qty</label>
<select name="qtyX_another_arbitrary_id" id="somethingElse">
   <option>10</option>
   <option>20</option>
</select>

How can I locate the correct select element without referencing them by ID?

//select[@id='???']

That would work great if I knew the ID ahead of time.  That’s where the ‘for’ attribute comes in handy:

//label[text()='Product']/@for

That will get me the for attribute of the “Product” label.  Putting it all together, it looks like this:

//select[@id=//label[text()='Product']/@for]

Cygwin and SVN—friends at last

February 23, 2010

I’m kind of a CLI junky. Ironically, I prefer command line over GUI for its simplicity (that and I haven’t found a pointing device I care to use). In a Windows world, short of setting up an ssh server, this pretty much leaves me with Cygwin or PuTTYcyg (no, cmd.exe doesn’t count for anything).

The problem

When we upgraded to Subversion 1.5 (and now 1.6), I started running into problems with the new interactive conflict management. I prefer not to use it, but occasionally forget to add the --non-interactive flag to my commands.

The Dos emulator handled this just fine, but Cygwin would lock up. With PuTTYcyg, I can ctrl-C to at least get my prompt back. Then I have to svn cleanup. Sometimes this can bork your svn meta data beyond what ‘cleanup’ can fix. There are few things more frustrating than spending good development time on fixing your development environment.

The Solution

It turns out, you can disable interactive-conflicts by default by modifying your Subversion client’s config file. On Windows, this is %APPDATA%\Subversion\config (“$APPDATA/Subversion/config” for PuTTYcyg users).

Because I started with svn 1.1, the commented options in my config were outdated so I did the following to get a fresh one:

rm "$APPDATA/Subversion/config"
svn --version

Any subversion command will (re-)create a default config file.
Finally, I just uncommented the following line:

# interactive-conflicts = no

Now I can run SVN in my CLI of choice with no worries of losing time to repairing my local code check-out.

UPDATE: svn/cygwin now happily interactive

A colleague of mine suggested simply installing svn through the cygwin package management (i.e. re-run cygwin’s setup.exe). After doing so, I can interact with svn through local puttcyg just as I would through normal ssh. (Among other things, I no longer have to worry about my credentials being echoed when svn prompts me for authentication!)

Omniture Web Services API Code For ColdFusion

December 10, 2009

We’ve been working on a ColdFusion application that uses the Omniture Web Services API. Omniture’s documentation is pretty light in this area, and the examples were not very applicable to ColdFusion. After pulling together several different blogs, docs, and some trial & error, I have created the following incantation. Hopefully it’ll save another Omniture/ColdFusion user a few minutes.

<cfsilent>
    <cffunction name="getOmnitureWsseRequestHeader" access="private" returntype="Any" output="false"
                hint="Return the header needed to make an Omniture web service request as a SOAPHeaderElement object">
        <cfargument name="strUsername" type="String" required="true" />
        <cfargument name="strSecret" type="String" required="true" />
        <cfscript>
            // Unique random number
            var strNonce = createUUID();
            // Date created in Omniture's required format
            var strCreated  = DateFormat(Now(),'YYYY-mm-dd H:mm:ss');
            // Password encoded according to Omniture's requirements
            var strPassword = ToBase64(Hash("#strNonce##strCreated##arguments.strSecret#", 'SHA').toLowerCase());
            // Start to build header
            var objHeader = CreateObject("java", "org.apache.axis.message.SOAPHeaderElement");
            var nodeUserToken = "";
            var nodePassword = "";
            // Construct XML structure with code
            objHeader.init("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "wsse:Security");
            objHeader.setMustUnderstand(1);
            objHeader.setActor("");
            nodeUserToken = objHeader.addChildElement("wsse:UsernameToken");
            nodeUserToken.setAttribute("wsu:Id", "User");
            nodeUserToken.addChildElement("wsse:Username").setValue(arguments.strUsername);
            nodePassword = nodeUserToken.addChildElement("wsse:Password");
            nodePassword.setAttribute("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0##PasswordDigest");
            nodePassword.setValue(strPassword);
            nodeUserToken.addChildElement("wsse:Nonce").setValue(strNonce);
            nodeUserToken.addChildElement("wsu:Created").setValue(strCreated);
        </cfscript>
        <cfreturn objHeader />
    </cffunction>

    <cfscript>
        objHeader = getOmnitureWsseRequestHeader("your-username", "your-secret-code");
        objService = CreateObject("webservice", "your-omniture-wsdl-file-url");
        objService.setHeader(objHeader);
    </cfscript>

</cfsilent>

<cfoutput>
<p>objService.companyGetTokenCount(): #objService.companyGetTokenCount()#</p>
</cfoutput>

To use this test code, do the following:

  1. Copy the code to a .cfm file
  2. Replace “your-username” and “your-secret-code” with your Omniture web service user name & shared secret code
  3. Replace”your-omniture-wsdl-file-url” with the URL to your .wsdl file
  4. Run it — it should work!

Don’t forget to use cfdump on the objService variable to see all of the methods you can call with the API!

Custom Fonts in ColdFusion

April 29, 2009

We have a couple non-standard fonts that we use for some of our our FPU class materials. For our new Coordinator Resource Center we wanted to use these same fonts in a server generated PDF file. However, this was the first time we’ve actually used a non-standard font with ColdFusion. This turned out to be a much more difficult task than it would seem. During this process I ran into a few issues. (These apply to ColdFusion MX 7.02)

Font Family vs Font Face
Within the True Type Font file, there are definitions for the “Font Family” and the “Font Face.” For some reason, ColdFusion would not read the file unless these were set to the exact same string. I used a font editor to modify these. In addition, if the font file contains different a “Windows Name” and “Mac Name” you might want to make those the same as well.

System vs. User Defined File Location
There is a difference in where you place the files. If you happen to be running ColdFusion on a Windows box, you’re better off just installing the fonts as you normally would through the control panel. Developing locally, I didn’t have any problems with that. However, our server runs Linux and I needed to set a custom path to tell ColdFusion where to look for the fonts. This is easily done through the Administrator control panel.

However, unless you have this hot fix installed, it will never read the fonts and never give you an error. The admin tool will show you they are installed, but when you try to generate a PDF using them, it will always fall back to a system font. The documentation on this hot fix gives you no indication that it will fix this problem, and it is not included in any other service pack.  However, this solved the problem for us.

Font Types
Supposedly, ColdFusion will support the following font types:
TTF (True Type Font)
TTC (True Type Collection)
OTF (Open Type Font)
AFM (Adobe Font Metrics)

However, I ran into an OTF font where (no matter what I did) I could not get the PDF to output that font. It would keep falling back to the system font. I ended up using [FontForge] to convert it from OTF to TTF.

How Much Memory Does This Take?

January 21, 2009

Ever wonder how much memory an object takes?

The web has lots of  great theoretical answers to that question.

Theories are great, and they give lots of great insights into how a Java compiler works. But what if things aren’t perfectly clean-cut? There are plenty of reasons to want proof including the following possibilities:

  • Say you use the Spring Framework or some other mechanism where your objects aren’t composed until runtime
  • Maybe you use a library/jar/package/swf/swc/etc, you don’t have access to the underlying code, and you’re trying to weigh its memory footprint
  • Maybe your objects are composed differently in different states
  • Maybe you use a Rapid Application Development language such as  ColdFusion or PHP and want to see how much overhead they bring along
  • Maybe you’re just a hard-core detailed person who wants to prove that the theory really matches reality

We’ve had to troubleshoot ColdFusion apps for memory usage, and we’ve found this approach to be helpful:

  1. Call for garbage collection (if applicable)
  2. Take a snapshot of how much memory is used
  3. Instantiate a whole bunch of copies of the class in question, and stuff them into an array
  4. Call for another garbage collection (if applicable)
  5. Take another snapshot of how much memory is used
  6. Subtract the first memory reading from the second, divide by the number of objects created, and you have a pretty good approximation of how much memory each object takes

As a side-note, the more objects you create in your test, the more your test will drown out background noise from other activity, and the more accurate your reading will be on each object.

After researching this method, we honed it a bit and made some useful discoveries. For example, in ColdFusion 7, each CFC instantiated takes up ~2kB. On top of that, each <cffunction></cffunction> in that cfc takes up an additional ~150 bytes.  So the abc.cfc “class” (found below) with 3 empty methods takes up ~2.3kB. (disclaimer: I’ve heard but not yet personally verified that memory usage improves in CF8 and beyond.)

In our scenario, we had CFCs inheriting a data persistence layer including about 50 methods, our CFCs included about 50 more in addition to member data in the variables scope. Without fully understanding the under-the-hood functionality of ColdFusion 7, we were instantiating 30 CFCs per user at 20kB each (~half a meg) for a few thousand users. Then we wondered why we kept eating through our available memory so quickly.

After making this discovery we spent a few days re-factoring some code hoping to use resources better. We limited the inheritance of the persistence layer to where it was necessary. We re-factored the code to use ColdFusion queries rather than CFCs (queries wound up being MUCH lighter than similar CFCs). When we launched the changes our application used about 40% less memory.

As hinted earlier, this algorithm can also be used to analyze memory usage by basic Java components. One question I’ve dabbled with is how much memory java code takes. I may cover that subject in another post someday, but for now I’ll leave that as an exercise for the reader. In the meantime, here’s a ColdFusion script that implements the algorithm described above.  Enjoy!

<!---this part executes in a cfm--->

<cfset runtime = CreateObject("java","java.lang.Runtime").getRuntime() />
<cfset myArray = arrayNew(1) />
<cfset intNumberOfObjectsCreatedPerLoop = 10 />
<cfset intTotalNumberOfObjectsCreated = 0 />
<cfset intX = 0 />
<cfset intBytesBeforeTest = 0 />
<cfset intBytesAfterTest = 0 />
<cfset intBytesCreated = 0 />
<cfset intBytesIn1MB = 1048576 />
<cfset memoryThresholdInMB = 50/>
<cfset obj = "" />

<cfset runtime.gc() />
<cfset intBytesBeforeTest = runtime.totalMemory() - runtime.freeMemory() />
<cfloop condition="intBytesCreated lt (intBytesIn1MB * memoryThresholdInMB)">
<cfset intTotalNumberOfObjectsCreated = intTotalNumberOfObjectsCreated +
		intNumberOfObjectsCreatedPerLoop />
	<cfloop from="1" to="#intNumberOfObjectsCreatedPerLoop#" index="intX">
		<cfset obj = CreateObject("component", "abc") />
		<cfset arrayAppend(myArray, obj) />
	</cfloop>
	<cfset intBytesAfterTest = runtime.totalMemory() - runtime.freeMemory() />
	<cfset intBytesCreated = intBytesAfterTest - intBytesBeforeTest />
</cfloop>
<cfoutput>
<cfset runtime.gc() />
#intBytesCreated / 1024# kilobytes of objects created.<br>
Each CFC takes approximately :
#(intBytesAfterTest - intBytesBeforeTest) / intTotalNumberOfObjectsCreated / 1024#kB
</cfoutput>
<!--- this part resides in a separate file (abc.cfc) in the same directory --->
<cfcomponent name="abc">
	<cffunction name="a" output="false"><!--- Really important code here. ---></cffunction>
	<cffunction name="b" output="false"><!--- Really important code here. ---></cffunction>
	<cffunction name="c" output="false"><!--- Really important code here. ---></cffunction>
</cfcomponent>