<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Orange is my favorite color &#187; My Software</title>
	<atom:link href="http://www.ghidinelli.com/feed?cat=3%2C5" rel="self" type="application/rss+xml" />
	<link>http://www.ghidinelli.com</link>
	<description></description>
	<lastBuildDate>Fri, 27 Jan 2017 17:45:50 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Java / ColdFusion SSL handshake_failure Fix</title>
		<link>http://www.ghidinelli.com/2017/01/27/java-coldfusion-ssl-handshake_failure-fix</link>
		<comments>http://www.ghidinelli.com/2017/01/27/java-coldfusion-ssl-handshake_failure-fix#comments</comments>
		<pubDate>Fri, 27 Jan 2017 17:45:50 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[ColdFusion]]></category>
		<category><![CDATA[Web/Internet]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1725</guid>
		<description><![CDATA[Fixing a Java/ColdFusion/JVM-based SSL handshake_failure when making an HTTPS request ]]></description>
			<content:encoded><![CDATA[<p>As PCI DSS rules and general security best practices evolve, more people are configuring their web servers to eliminate less-secure versions of SSL and TLS for their HTTP connections. If you are using a Java or JVM-based application, this may manifest itself in a sudden inability to connect resulting in a fatal handshake_failure. </p>
<p>What this is telling you is that you were able to connect but your client and the remote server were unable to find a mutually acceptable encryption algorithm to communicate over so further communication was aborted.</p>
<p>An easy way to tell if this is your problem as a Java/JVM client is to use the <a href="https://www.ssllabs.com/ssltest/analyze.html">Qualys SSL analyzer</a> and see whether it reports a Java client can connect. In my case, I saw the following failures when I entered the SSL endpoint I wanted to access:</p>
<p><a href="https://www.ghidinelli.com/wp-content/uploads/2017/01/java-ssl-handshake-failure.png"><img src="https://www.ghidinelli.com/wp-content/uploads/2017/01/java-ssl-handshake-failure.png" alt="" title="java-ssl-handshake-failure" style="width: 100%; max-width: 972px;" class="aligncenter size-full wp-image-1726" /></a></p>
<p>If you happen to be a ColdFusion user, you can add <tt>-Djavax.net.debug=ssl,handshake,verbose</tt> to your jvm.config, restart and make your HTTPS request and see the full list of ciphers being attempted and the ultimate failure in your log file. My logs with the handshake_failure can be seen in <a href="https://gist.github.com/ghidinelli/2768e0b28d767a5b018fd3f01cdc3e9f">this gist</a> for comparison.</p>
<h2>Fix with Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files</h2>
<p>The fix is easy to implement but not easy to find. I experienced this with Oracle&#8217;s JVM and not the OpenJDK so other JVM implementations may behave differently. In the case of Oracle&#8217;s JVM, strong encryption is disabled out of the box for export reasons so the JVM can not use an encryption algorithm stronger than 128-bit. Many web servers are now disabling 128-bit SSL in favor of 256-bit and 384-bit encryption algorithms so the fix is to turn these on in your JVM.</p>
<ol>
<li>Download the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files from Oracle (this link is for Java 1.8, Google for alternative JVM versions): <a href="http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html">http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html</a></li>
<li>Extract the two JAR files into your <tt>jre/lib/security</tt> folder overwriting the US_export_policy.jar and local_policy.jar.</li>
<li>Restart the JVM</li>
</ol>
<p>You should now be able to connect to the remote server. If you&#8217;re still unable to connect, try enabling debugging and see which ciphers are attempted/ignored. The remote server may have an exotic configuration that requires you to contact them but the unlimited strength jurisdiction policy files should fix handshake_failure issues.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2017/01/27/java-coldfusion-ssl-handshake_failure-fix/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Preventing Long ColdFusion Initialization Timeouts</title>
		<link>http://www.ghidinelli.com/2015/11/21/preventing-long-coldfusion-initialization-timeouts</link>
		<comments>http://www.ghidinelli.com/2015/11/21/preventing-long-coldfusion-initialization-timeouts#comments</comments>
		<pubDate>Sat, 21 Nov 2015 17:08:19 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[ColdFusion]]></category>
		<category><![CDATA[Web/Internet]]></category>
		<category><![CDATA[devops]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1696</guid>
		<description><![CDATA[Request timeouts when initializing your ColdFusion app? This function will stop them.]]></description>
			<content:encoded><![CDATA[<p>My apps use a variety of frameworks which offer a lot of rapid development and agility but they also come at the cost of slower startup times. For a Coldspring-based app with a large service layer, you could easily run into timeouts when initially loading the application. Here&#8217;s a simple function to temporarily extend the processing timeout during initialization using CFTHREAD and the Admin API:</p>
<pre><code>&lt;cffunction name="extendRequestTimeoutDuringInit" output="false" access="public" returntype="any"&gt;

  &lt;cfthread action="run" name="delayRequestTimeoutDuringInit"&gt;
    &lt;cfset thread.adminapi = createObject("component", "cfide.adminapi.administrator") /&gt;
    &lt;cfset thread.adminapi.login('mysecretpassword') /&gt;
    &lt;cfset thread.runtime = createObject("component", "cfide.adminapi.runtime") /&gt;
    &lt;cfset thread.timeout = thread.runtime.getRuntimeProperty("TimeoutRequestTimeLimit") /&gt;
    &lt;cflog file="application" text="Extending RequestTimeout to #2*thread.timeout# seconds" /&gt;
    &lt;cfset thread.runtime.setRuntimeProperty("TimeoutRequestTimeLimit", 2*thread.timeout) /&gt;
    &lt;cfset sleep(2 * thread.timeout * 1000) /&gt;
    &lt;cfset thread.runtime.setRuntimeProperty("TimeoutRequestTimeLimit", thread.timeout) /&gt;
    &lt;cflog file="application" text="Restored RequestTimeout to #thread.timeout# seconds" /&gt;
  &lt;/cfthread&gt;

&lt;/cffunction&gt;</code></pre>
<p>I call this in Server.cfc when my instance starts up but you could also call it from any reinit routine in OnRequestStart or OnApplicationStart. Previously when we pushed code, the first user request would kick off the initialization process while other requests queued. Many of those first requests would exceed our page timeout setting of 60 seconds. Running the above function uses a background thread to double the timeout and later reset it once the application has initialized so users no longer see timeout/error screens.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2015/11/21/preventing-long-coldfusion-initialization-timeouts/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Jstat in Java 1.8 Can&#8217;t Find Process Run as Root</title>
		<link>http://www.ghidinelli.com/2015/08/20/jstat-in-java-1-8-cant-find-process-run-as-root</link>
		<comments>http://www.ghidinelli.com/2015/08/20/jstat-in-java-1-8-cant-find-process-run-as-root#comments</comments>
		<pubDate>Thu, 20 Aug 2015 17:55:29 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[ColdFusion]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Web/Internet]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[jvm]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1649</guid>
		<description><![CDATA[Jstat not working under Java 1.7 u75 or higher including Java 8? There's a bug! Here's how to work around it to get your JVM details.]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve written a handful of times on <a href="https://www.ghidinelli.com/2009/07/16/finding-memory-leaks-coldfusion-jvm">observing and tuning the JVM</a> using tools such as Jstat. I recently upgraded our servers to Java 8 and found that my Jstat script could no longer find the process no matter what I tried.</p>
<p>It turns out <a href="https://bugs.openjdk.java.net/browse/JDK-8073858">there is a bug</a> from JDK 1.7 u75 onwards that is present at least through JDK 1.8 u60 which prevents Jstat, when run as root, from introspecting JREs running as other users.</p>
<p>To solve, simply run jstat as the user who owns the JRE process.  You can pass the username to sudo when running jstat like:</p>
<p><code>sudo -u nobody jstat -gcutil -t &lt;pid&gt; 1s 30</code></p>
<p>And you&#8217;ll be treated to your output again. In my case, I have two server instances running so I like to see their output side by side at the console.  Here&#8217;s my script for doing that using /usr/bin/paste and some console redirection trickery:</p>
<p><script src="https://gist.github.com/ghidinelli/a57dd23c03005f0980e8.js"></script></p>
<p>The username can be figured out from the process but this was a quick fix for the above bug.  </p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2015/08/20/jstat-in-java-1-8-cant-find-process-run-as-root/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Redirecting ColdFusion logs in a Docker Container</title>
		<link>http://www.ghidinelli.com/2015/07/06/redirecting-coldfusion-logs-docker-container</link>
		<comments>http://www.ghidinelli.com/2015/07/06/redirecting-coldfusion-logs-docker-container#comments</comments>
		<pubDate>Mon, 06 Jul 2015 18:35:34 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[ColdFusion]]></category>
		<category><![CDATA[Web/Internet]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1637</guid>
		<description><![CDATA[Learn how to redirect ColdFusion logs to stdout and stderr on Linux or OS X.  Especially useful in containerization using Docker to centralize logs.]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been modernizing our development environment and started with the work by <a href="https://github.com/finalcut/docker-coldfusion10">Bill Rawlinson</a> to create a portable ColdFusion Docker image. If you&#8217;re new to Docker and the benefits of containerization, read up <a href="http://readwrite.com/2014/12/23/docker-to-dominate-in-2015">why Docker is dominating 2015</a>. We have Docker Compose running a stack with ColdFusion, Nginx, Postgres, ActiveMQ and Redis all linked together and the entire setup can be started with one command:</p>
<p><code>docker-compose up -d</code></p>
<p>I&#8217;m simultaneously moving from a Windows machine to a Mac and I&#8217;ve been really missing the console logging for watching application activity and errors.  It&#8217;s easy when installed locally to tail the coldfusion-out.log file but once containerized you don&#8217;t have that luxury (without using docker exec to log in to the running container which is a bad practice).  The issue is on Linux, ColdFusion has no option to log to stdout like on Windows.  As a result, the docker logs command can&#8217;t see the output from ColdFusion once the container is running.  </p>
<p>It turns out there is a very simple and very elegant way to force ColdFusion, or any app which wants to daemonize and write to log files, to write to stdout.  From this <a href="http://serverfault.com/questions/599103/make-a-docker-application-write-to-stdout">ServerFault thread</a> referencing the Nginx Dockerfile, you can symlink the coldfusion-out.log file to /dev/stdout:</p>
<pre><code>ln -sf /dev/stdout /opt/coldfusion10/cfusion/logs/coldfusion-out.log</code></pre>
<p>My Dockerfile for ColdFusion has these two lines:</p>
<pre><code># redirect logs so `docker logs -f &lt;container&gt;` works
RUN ln -sf /dev/stdout /opt/coldfusion10/cfusion/logs/coldfusion-out.log
RUN ln -sf /dev/stderr /opt/coldfusion10/cfusion/logs/coldfusion-err.log</code></pre>
<p>Now the logs can be collected and observed in a centralized location using <a href="https://docs.docker.com/articles/dockerfile_best-practices/">Docker best practices</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2015/07/06/redirecting-coldfusion-logs-docker-container/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Talking SaaS at dev.Objective()</title>
		<link>http://www.ghidinelli.com/2015/05/03/talking-saas-at-dev-objective</link>
		<comments>http://www.ghidinelli.com/2015/05/03/talking-saas-at-dev-objective#comments</comments>
		<pubDate>Sun, 03 May 2015 23:41:49 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[ColdFusion]]></category>
		<category><![CDATA[Web/Internet]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1624</guid>
		<description><![CDATA[Brian returns to dev.Objective() in Minneapolis to present on Building SaaS Applications]]></description>
			<content:encoded><![CDATA[<p><strong>Update</strong> &#8211; Slides from my talk were <a href="http://www.slideshare.net/ColdFusionConference/ghidinelli-building-saasappsfinal">posted on SlideShare</a>.  Thanks to everyone who came and all the tweets!</p>
<p>I&#8217;ll be speaking at <a href="http://www.devobjective.com/">dev.Objective()</a> this year in Minneapolis on a hybrid business/programming topic titled, &#8220;<a href="http://www.devobjective.com/sessions/building-multi-tenant-saas-applications/">Building Multi-Tenant Software-As-A-Service (Saas) Applications</a>&#8221;  This will be a comprehensive review covering the hurdles I&#8217;ve crossed growing <a href="http://www.motorsportreg.com">MotorsportReg.com</a> from a one-man nights-and-weekends operation to a team that has processed more than $100,000,000 in event entry fees.  The talk will cover:</p>
<ul>
<li>Database design and tenancy model trade-offs</li>
<li>Third party services and designing for resiliency</li>
<li>E-commerce strategies for cards on file, split payments, multiple currencies and marketplace-style apps</li>
<li>Implications of going global including i18n, l10n, currency and domiciling</li>
<li>Cross-customer security and the impact of banking and legal requirements such as PCI DSS, IRS and Know-Your-Customer (KYC)</li>
<li>Code strategies for implementing customer or domain-specific functionality without a &#8220;kitchen sink&#8221; user experience problem</li>
</ul>
<p>Developers who are interested in SaaS apps or have the entrepreneurial itch themselves will walk away with a roadmap of the issues a SaaS app must solve and strategies to achieve success.  I&#8217;ll be speaking immediately after the keynote Wednesday morning, May 13, at 10:15am. </p>
<p>This is my second time attending dev.Objective() and I&#8217;m returning because I got a ton of value from it.  I hope you&#8217;ll consider attending if you&#8217;re not already signed up as it will be a positive boost for your creativity and skills.   Registration is <a href="https://www.signup4.net/public/ap.aspx?EID=CFOB19E&#038;TID=7R1b5j8%2bsw2owJ95UBUTMw%3d%3d">still available for $999</a>.</p>
<p>Connect with me on twitter ahead of the conference to keep in touch at <a href="http://twitter.com/ghidinelli">@ghidinelli</a>.  You can connect with other speakers and attendees on <a href="http://lanyrd.com/2015/devobjective/">Lanyrd</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2015/05/03/talking-saas-at-dev-objective/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Adding 4000 subscribers in 45 days</title>
		<link>http://www.ghidinelli.com/2014/09/01/4000-new-subscribers-in-45-days</link>
		<comments>http://www.ghidinelli.com/2014/09/01/4000-new-subscribers-in-45-days#comments</comments>
		<pubDate>Mon, 01 Sep 2014 21:49:14 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[Business]]></category>
		<category><![CDATA[Web/Internet]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1598</guid>
		<description><![CDATA[How 45 days of "testing" grew my email list by 53% after 9 years of "implementing".]]></description>
			<content:encoded><![CDATA[<p>I love researching things.  Learning about something new and finding the best way to approach a problem feeds the engineering part of my background.  I satisfy that by reading a ton and because of my online businesses, I read a lot of SaaS-related online marketing and sales material.   But sometimes I read more than I act.  As a recent example, in setting up the best possible go-to-market strategy for our new live timing app, <a href="http://racehero.io">RaceHero</a>, I ran out of time before vacation and lost weeks out of my marketing strategy by failing to kick it off before I left town.  That same &#8220;cobbler&#8217;s shoes&#8221; fate had befallen my email lists for the past 9 years.</p>
<p>This year I&#8217;ve been working to do more A/B testing.  A/B tests are mathematically-backed competitions between two or more options which are scored by the actions of the users.  They may also be the <a href="https://training.kalzumeus.com/newsletters/archive/why_you_do_not_ab_test">single highest ROI tactic for software companies</a>. These tests could be anything from a simple color change of a button (shocking, to what extent that can matter) all the way to a completely different web page.  I was first exposed to the technique back in 2001 at Yahoo! when I was helping redesign their search results but it wasn&#8217;t until the last 18 months I ran the first A/B test on <a href="http://www.motorsportreg.com">MotorsportReg.com</a>.  </p>
<p>Which brings me to how I grew one of my mailing lists by 53% in 45 days.  In the history of this 9-year old list, the last two months would make a hockey stick look like a rolling foothill.  This is like the face of a glacier where it meets into the ocean: a vertical line that reads like an error.</p>
<p>The technique is perhaps too simple to be valuable by itself: I put the signup form where many more people would see it in their routine use of our platform.  Previously, we had a few CTAs in our transactional emails and on our event calendar but the actual signup took place on a mailing lists screen under &#8220;My Account&#8221;.  Few people went there looking to add more email to their inbox so list growth was steady but slow.  Now, when users create a new account or reconfirm their details, they see checkboxes at the bottom of their personal information letting them opt-in to the lists.  Thousands of people see these screens every month and because the lists are valuable, thousands of them are signing on.</p>
<h2>&#8220;It was just a test&#8221;</h2>
<p>The obvious question is why didn&#8217;t we do this sooner?  The answer is because I wasn&#8217;t sure if these transactional flows were appropriate places to insert a list signup.  I had concerns that it might negatively impact registration conversion.  So I hemmed.  And I hawed.  And I periodically looked at the issue in our bug tracker but never quite got over the &#8220;ewwwww&#8221; feeling to put it in place.</p>
<p>That&#8217;s the beauty of A/B testing.  A feature request in our bug tracker is a stone being added to a wall with mortar: it becomes a seemingly permanent piece of your architecture, of your user interface and of your responsibility.  But an A/B test, why, <em>that&#8217;s just a test</em>!  We have made no commitments to keeping it around.  We&#8217;re not even sure we like it!   Implementation only took a few hours so we could rip it out at any time for any reason and respond, &#8220;It was just a test.&#8221;   Simply doing it, rather than talking about it, is <a href="http://lucasartoni.com/2014/08/28/the-rules-of-ab-testing-by-tyler-durden/">rule #2 of A/B testing fight club</a>.</p>
<p>Since it was just a test, we hoped for a nice bump to justify keeping it around.  We were blown away.  At the current rate, we will reach 25,000 subscribers (333% growth) by the end of 2015.  And because we have a large percentage of first-time participants come through our service, we should see a permanently scaled growth rate.   As an additional bonus, this list drives participation for our events so we&#8217;re creating a positive feedback loop for our event organizers and our bottom line too.</p>
<p>All because of a feature I was skeptical of &#8220;implementing&#8221; but happy to &#8220;test&#8221;.</p>
<p>Remember: making the call is making progress.   Doing is better than planning.   Execution is more valuable than ideas.   Look at your to-do list and find one or two things you&#8217;ve been putting off.  What can you do to &#8220;test&#8221; it (whatever that might mean in your case) to move it ahead and gain confidence in your choice?  </p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2014/09/01/4000-new-subscribers-in-45-days/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Good APIs should accept foreign keys</title>
		<link>http://www.ghidinelli.com/2014/06/24/good-apis-should-accept-foreign-keys</link>
		<comments>http://www.ghidinelli.com/2014/06/24/good-apis-should-accept-foreign-keys#comments</comments>
		<pubDate>Tue, 24 Jun 2014 22:26:01 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[Web/Internet]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1594</guid>
		<description><![CDATA[Writing an API that accepts data from other systems?  Provide a searchable foreign key attribute to simplify integrating for all of your developers.]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m automating online marketing for event organizers at <a href="http://www.motorsportreg.com/index.cfm/event/event-management">MotorsportReg.com</a> by synchronizing event listings with third party calendars.  I&#8217;m working with a typical API which is modeled such that you create a venue, they return an ID and then you use that ID in your event creation call.  Pretty standard stuff.  </p>
<p>The challenge is when I want to update or synchronize the listings later.  The search makes it difficult to identify an event or venue that I&#8217;ve created in the system vs. one that someone else has created (which may have different attributes) or determine if I&#8217;m selecting the matching item at all (do we trust a string comparison on the name?)  The venues in this API do support an arbitrary properties collection where I could stash an ID from our system but you can&#8217;t search based on those properties which results in searching, then looping to see if a property matches our original request and, if not, creating a new venue.</p>
<p>It doesn&#8217;t have to be that tedious.  As a rule of thumb, any API which allows the creation of a logical object (people, events, places, etc) where the record of authority may originate from another system should accommodate a foreign identifier as a standard property and allow searching against the identifier.</p>
<p>I&#8217;m sure there are APIs which follow this practice but it should be a standard.  Simplify my integration efforts so that I can easily send and synchronize data with you.  Keep us loosely coupled.  It&#8217;s one more field on your end or it&#8217;s a ton of code on my end that results in a more fragile relationship.  Don&#8217;t make me maintain my own database table of ID mappings.  </p>
<p><strong>Update May 2015</strong> &#8211; Since writing this, I&#8217;ve found another dozen instances where this would be valuable and plan to bake this into the second version of our API.  However, I&#8217;ve come to believe that webhooks are an alternative 80% solution.  Rather than polling and synchronizing, receiving a webhook when events of interest take place saves me the typical flow of grabbing a set of records, looping over them and comparing them locally for changes.  The best of both worlds would be for the webhook payload to include my foreign key.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2014/06/24/good-apis-should-accept-foreign-keys/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Selectively applying Hubspot (or other) tracking code</title>
		<link>http://www.ghidinelli.com/2014/01/27/selectively-applying-hubspot-tracking-code</link>
		<comments>http://www.ghidinelli.com/2014/01/27/selectively-applying-hubspot-tracking-code#comments</comments>
		<pubDate>Tue, 28 Jan 2014 06:02:29 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[Web/Internet]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1566</guid>
		<description><![CDATA[Using jQuery to selectively apply Hubspot or other Javascript tracking codes to users]]></description>
			<content:encoded><![CDATA[<p>My company uses <a href="http://www.hubspot.com">Hubspot</a> to help support our marketing efforts.  One thing Hubspot provides is great tracking that shows all of the touch points you have with a potential customer as they read your blog, download your content and fill out your forms.  This helps form a cohesive picture of where someone is in the sales funnel and their interests and needs.</p>
<p>As a Software-as-a-Service for <a href="http://www.motorsportreg.com">online event registration</a>, however, we face the dilemma of separating out potential customers of our SaaS app and <em>their</em> customers who come to our site to also participate in the marketplace.  For every event organizer who visits our site and might be interested in using our system, hundreds of event attendees come to get registered.  Including the <a href="http://knowledge.hubspot.com/getting-started-with-hubspot/how-to-install-the-tracking-code-on-a-generic-website">Hubspot tracking code</a> on our home page makes our traffic numbers look great but has the negative side effect of dropping our lead and conversion ratios well under 1%.  Ignore for a moment that this is demoralizing, it also makes reports difficult to use.  Picture comparing the success of a landing page with 0.05% conversions and another with 0.07%.  They both look effectively equivalent because of the scale but if those numbers instead read 50% and 70%, you would not only be impressed but recognize the latter is performing 40% better!</p>
<p>One option here would be to simply remove the tracking code from the home page and that would fix the statistic and volume issues.  But we would lose important referring data.  While Google is no longer giving us much in the way of search keywords, we still can see referring URLs when people surf to MotorsportReg.com.  If we removed the tracking code from the home page and cookie&#8217;d them when they hit the About Us page, we would lose that original traffic source.</p>
<p>So, the challenge began to find a way to cookie the people who were potential leads and do it on the home page so we could capture referrer data.  I asked our sales guy, our onboarding rep and customer support.  Nobody had any idea how to segment the traffic to weed out the event attendees from skewing our data.  </p>
<p>But I had an idea, and it turns out it works pretty well.</p>
<p>What I want to know, specifically, is when someone views our &#8220;<a href="www.motorsportreg.com/index.cfm/event/event-management">How it works</a>&#8221; or another marketing/sales page as that indicates interest in our SaaS product.  I started by tagging all links in the top and bottom navigation that link to our marketing and sales pages with a CSS class like:</p>
<p><code>&lt; a href="/about" class="hs"&gt;How it Works&lt; /a&gt;</code></p>
<p>On those marketing pages, we include the Hubspot tracking code on every view.  But for the home page, I wrapped it inside of a jQuery click handler:</p>
<p><code>$(document).ready(function()<br />
{<br />
  // dynamically load hubspot code only if someone clicks a marketing-related link on the home page<br />
  $('.hs').click(function(e)<br />
  {<br />
    var anchor = $(this), h;<br />
    h = anchor.attr('href');<br />
    if (typeof _hsq === "undefined")<br />
    {<br />
      e.preventDefault();<br />
      $.getScript('//js.hubspot.com/analytics/'+(Math.ceil(new Date()/300000)*300000)+'/{your-hubspot-id}.js', function(){<br />
        setTimeout(function(){window.location = h},1000);<br />
      });<br />
    }<br />
  });<br />
});</code></p>
<p>The code basically says: if someone clicks a link with the class &#8220;hs&#8221;, grab the link they clicked, load the Javascript file from Hubspot&#8217;s servers, wait 1 second, and then continue to the original link.  In the 1 second while we sleep, the code should load from Hubspot&#8217;s servers which results in the user getting cookie&#8217;d appropriately and Hubspot can still access the referring URLs, search keywords, etc.</p>
<p>This approach is not only limited to Hubspot.  It would work equally well with any Javascript-based tracking code like Google Analytics or others if you had some reason to exclude certain users from participating under certain circumstances.</p>
<p>It&#8217;s not perfect &#8211; the one second pause is too long in some cases, not long enough in others, and adds a delay in all cases which <a href="http://blog.kissmetrics.com/loading-time/">can increase page abandonment</a>.   However, we&#8217;ve been using this in production now for about 3 months and it&#8217;s been working well.  Lead volume is slightly up despite the brief delay and we see the full browsing history for contacts in Hubspot.  Our lead and traffic numbers are now properly scaled so we can make sense of the data we&#8217;re seeing.  </p>
<p>If you have multiple audiences viewing your site and only want to apply tracking to some of them, this technique is worth taking a look at.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2014/01/27/selectively-applying-hubspot-tracking-code/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CFPAYMENT now on GitHub</title>
		<link>http://www.ghidinelli.com/2013/06/05/cfpayment-now-on-github</link>
		<comments>http://www.ghidinelli.com/2013/06/05/cfpayment-now-on-github#comments</comments>
		<pubDate>Wed, 05 Jun 2013 17:05:29 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[ColdFusion]]></category>
		<category><![CDATA[My Software]]></category>
		<category><![CDATA[Web/Internet]]></category>
		<category><![CDATA[cfpayment]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1557</guid>
		<description><![CDATA[CFPAYMENT, the open source payment processing library for ColdFusion, is now hosted on GitHub for your forking pleasure]]></description>
			<content:encoded><![CDATA[<p>I see people in ColdFusion land still reinventing the wheel in writing payment gateways.  Stop!  Believe me when I tell you there are 502 ways for a payment request to fail and I&#8217;ve experienced every single one of them!  The open source payment processing library, CFPAYMENT, has processed tens of millions of dollars in production applications over the past 5 years.  Either use one of our supported gateways (including the very cool <a href="http://www.stripe.com">Stripe</a>) or write a tiny amount of code to translate requests from your gateway of choice.</p>
<p>To encourage you to leverage rather than rebuild, I&#8217;ve moved the source to GitHub where you can fork and pull:</p>
<p><a href="https://github.com/ghidinelli/cfpayment">https://github.com/ghidinelli/cfpayment</a></p>
<p>There&#8217;s also a <a href="https://groups.google.com/forum/?fromgroups#!forum/cfpayment">Google group</a> where I provide support if you want to use a gateway we don&#8217;t yet include.  I&#8217;ll help you understand how to use the library and extend it so you don&#8217;t go through the pain of learning the hard way how transactions can fail in production.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2013/06/05/cfpayment-now-on-github/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Turbo Stack Traces for Tough Debugging</title>
		<link>http://www.ghidinelli.com/2013/04/02/turbo-stack-traces-for-tough-debugging</link>
		<comments>http://www.ghidinelli.com/2013/04/02/turbo-stack-traces-for-tough-debugging#comments</comments>
		<pubDate>Tue, 02 Apr 2013 21:51:09 +0000</pubDate>
		<dc:creator>brian</dc:creator>
				<category><![CDATA[ColdFusion]]></category>
		<category><![CDATA[Web/Internet]]></category>
		<category><![CDATA[jvm]]></category>

		<guid isPermaLink="false">http://www.ghidinelli.com/?p=1547</guid>
		<description><![CDATA[Here&#8217;s a guerilla debugging tactic based upon an old post of mine, Finding Memory Leaks in Coldfusion JVM, and some inspiration from Charlie Arehart.  I&#8217;m in the process of migrating from ColdFusion 8 to 10 and am encountering some unexpected errors.  This stack trace snippet should strike fear into the heart of any [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s a guerilla debugging tactic based upon an old post of mine, <a href="https://www.ghidinelli.com/2009/07/16/finding-memory-leaks-coldfusion-jvm">Finding Memory Leaks in Coldfusion JVM</a>, and some inspiration from <a href="http://www.carehart.org/blog/client/index.cfm/2009/6/24/easier_thread_dumps">Charlie Arehart</a>.  I&#8217;m in the process of migrating from ColdFusion 8 to 10 and am encountering some unexpected errors.  This stack trace snippet should strike fear into the heart of any developer:</p>
<pre><code>ava.lang.StackOverflowError at org.apache.xerces.dom.ParentNode.item(Unknown Source) at
net.sf.saxon.dom.NodeWrapper$ChildEnumeration.skipFollowingTextNodes(NodeWrapper.java:1166) at
net.sf.saxon.dom.NodeWrapper$ChildEnumeration.next(NodeWrapper.java:1194) at
net.sf.saxon.tree.util.Navigator$EmptyTextFilter.next(Navigator.java:918) at
net.sf.saxon.tree.util.Navigator$DescendantEnumeration.advance(Navigator.java:1052) at
net.sf.saxon.tree.util.Navigator$DescendantEnumeration.advance(Navigator.java:1111) at
net.sf.saxon.tree.util.Navigator$BaseEnumeration.next(Navigator.java:949) at
net.sf.saxon.tree.util.Navigator$DescendantEnumeration.advance(Navigator.java:1043) at
net.sf.saxon.tree.util.Navigator$BaseEnumeration.next(Navigator.java:949) at
... (endless repeat)</code></pre>
<p>Something is stuck in a loop but because the stack trace fills up, you can&#8217;t see any of the lines of code being executed just before the ka-blammo.  So, what to do?  </p>
<h2>Brute Force Debugging</h2>
<p>My solution was to swipe a perl script <a href="http://unix.stackexchange.com/questions/43185/run-unix-command-precisely-at-very-short-intervals-without-accumulating-time-lag">to run commands at very short intervals</a> and dump jstack output into a series of text files.  Low-tech, but effective.  This is the timer.pl script:</p>
<pre><code>#!/usr/bin/perl

use strict;
use warnings;
use Time::HiRes qw/time sleep/;

sub launch {
    return if fork;
    exec @_;
    die "Couldn't exec";
}

$SIG{CHLD} = 'IGNORE';

my $interval = shift;
my $start = time();
while (1) {
    launch(@ARGV);
    $start += $interval;
    sleep $start - time();
}</code></pre>
<p>And a separate shell script, dump.sh, so the redirect would create a new file every time it was run.  9892 is just the process id of the ColdFusion instance:</p>
<pre><code>#!/bin/sh
sudo -u nobody /usr/java/latest/bin/jstack 9892 &gt; t_$(date '+%Y%m%d%H%M%S_%N')</code></pre>
<p>I tried running it every second, but that wasn&#8217;t enough to catch the stack overflow in the act.  I upped it to every 500ms, 100ms and finally 50ms until it caught the offending code.  Here&#8217;s the command in action:</p>
<pre><code>perl timer.pl 0.05 ./dump.sh </code></pre>
<p>That says run the dump.sh every 50ms which will output to files formatted like t_20130402171521_009840232 using the current date and time.</p>
<h2>Results</h2>
<p>Running every 50ms, it doesn&#8217;t take long to fill up a directory so I started the script on the command line and in my web browser, triggered the offending code.  Here&#8217;s what my directory looks like:</p>
<pre><code> 52 t_20130402171519_959011475
 52 t_20130402171520_009648175
 52 t_20130402171520_060316767
 52 t_20130402171520_110450371
 52 t_20130402171520_160301392
 52 t_20130402171520_210357359
 52 t_20130402171520_260432922
 52 t_20130402171520_309171913
 52 t_20130402171520_360033048
 52 t_20130402171520_409978373
 52 t_20130402171520_459903958
 52 t_20130402171520_510488116
 52 t_20130402171520_558777256
 60 t_20130402171520_610244244
 72 t_20130402171520_659494494
136 t_20130402171520_709058569
136 t_20130402171520_760286651
136 t_20130402171520_810351161
136 t_20130402171520_860382457
136 t_20130402171520_910136459
136 t_20130402171520_960370954
136 t_20130402171521_009840232
 72 t_20130402171521_059986004
136 t_20130402171521_110340195
136 t_20130402171521_160277982
136 t_20130402171521_209090149
 84 t_20130402171521_260090368
136 t_20130402171521_310428721
136 t_20130402171521_367027484
136 t_20130402171521_410303418
136 t_20130402171521_460314216
136 t_20130402171521_509053355
136 t_20130402171521_560120477
136 t_20130402171521_610417764
128 t_20130402171521_677647036
136 t_20130402171521_712667159
136 t_20130402171521_758884870
136 t_20130402171521_810562747
136 t_20130402171521_860425517
...</code></pre>
<p>That&#8217;s kilobytes in the first column, filename in the second.  You can see the steady state while I&#8217;m going to push the button in the web browser and then very quickly the stack traces get larger as code is executing.  What I wasn&#8217;t getting until I stepped down to 50ms iterations were the intermediate 72kb and 84kb stack trace which actually showed the offending lines between the stack overflows churning.</p>
<p>While this hasn&#8217;t solved the problem yet, at least I know where to go looking (and it&#8217;s at <em>you</em>, XmlSearch()) to continue the troubleshooting process.</p>
<h2>Conclusion</h2>
<p>This is a pretty hacky solution but if you&#8217;re this far into the deep end of the &#8220;what do I do?&#8221; pool, it&#8217;s a useful technique.  A line debugger would be far better but I can&#8217;t replicate this in my test environment.  So, forget the &#8220;right&#8221; way to do it and let&#8217;s just get <em>some</em>  useful information so we can make progress.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ghidinelli.com/2013/04/02/turbo-stack-traces-for-tough-debugging/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
