Tuesday, May 14, 2013

Prevent SQL Server Transaction Log Getting Full in Dev

I routinely forget this and then have to look it up again so I'm putting it here so I can remember it:

If you have a DEVELOPMENT database using SQL Server you can prevent your transaction log from getting "full" and thus requiring you to back it up with a simple command:

alter database {DBNAME} set recovery simple;

Just make sure you only use that in development - if you use it in production make sure you're aware of the limitations and the issues surrounding using SIMPLE logging. You can learn more about the transaction log on the MSDN Site.

Wednesday, March 13, 2013

Coldfusion Salt and Hash

This isn't particularly ground breaking or even noteworthy - I mean people have been salting and hashing passwords for quite a while - but I figured I would put this coldfusion component out there to help people who haven't done salting and hashing in coldfusion before.

If you aren't really comfortable with the terms salting and hashing or salted hash - then please read this great article on the topic.

I'm not really going to go into any detail on the component here - instead I'll just point you to the github repository where you can read the readme and checkout the source code.

Please provide feedback if you see something incredibly stupid in the code - or better yet submit a pull request.


Thursday, February 28, 2013

Single Click Deploy of a Coldfusion Project from ANT

Sometimes I'm way behind the power curve.  Today is one of those days.  I've been using ant and cruise control for years to do some continuous integration testing of some ColdFusion projects but until today I didn't have any where there was a single click deploy process.

The project I set it up on is being managed in subversion - so if you use Git or some other version control system you'll have to adapt this to suit your own needs.

Before I start sharing the ant scripts here is some important background information you need to know and understand in order to actually make sense of the ant stuff.  First up is our branching and tagging policy for the project.

We use trunk as the main development branch.  We don't do big feature development on it but any quick bug fixes or what-not end up happening on trunk.  We also have a UAT and a PRODUCTION branch.  New features that our customer wants to test before delivery get merged from trunk to the UAT branch.  New features are developed on feature branches but once they are ready for UAT they get merged into TRUNK and then merged into UAT.  Once the feature is finished in the feature branch and we are making simple bug fixes to the features we typically make those just in TRUNK and merge to UAT.

We don't merge into production until things have cleared UAT.  Once we are ready to merge into UAT we make one final edit - to a file in the root of the projects directory called application.version.tag.cfm - this file just contains the new version number for the application.  This version number will also be used to create a release tag in subversion as part of the deployment process.  Once that version file is updated and commited we then merge all of the relevant changes from trunk to production for that release.  It is really important to have good comments and, ideally, issue numbers related to each commit for that feature set that is about to be pushed to production.

Once the merge into production is done and we are confident we want to push the changes to production we go to our continuous integration server (we recently moved from cruisecontrol to jenkins) and fire off the production job for that project.

The production job does the following things:
  1. updates the local production test webserver with the latest version of the production branch
  2. runs our suite of tests against the production test webserver
  3. if the tests pass...
  4. reads in the contents of application.version.tag.cfm to get the version number
  5. checks to see if a tag in subversion already exists for that version number
  6. if the tag exists the build stops and the user is prompted to either update the version number or manually delete the tag from subversion.
  7. if the tag doesn't exist... 
  8. create a tag in subversion based on the production branch named the same as the version number
  9. export the newly created tag to a staging directory
  10. delete any subdirectories or files we manage in subversion but which we don't deploy to production
  11. create a zip file of the remaining tag export files/directories
  12. delete the exported files that remain but keep the zip handy
  13. copy the zip file to a place where the customer can get it*
  14. delete the zip file from the staging location
  15. send out an email to the customer (and us) letting folks know the production zip file is ready.
The purists among you may say - wait this isn't a one click deploy - production isn't actually updated by this. And to you I say, "I know.  It sucks but I must work within the limitations I have"

The customer only lets us connect to them via special laptops they provide and only when those machines are connected to their vpn using a timed key token.  There just isn't a practical way to connect to them, then map to their webserver, and then copy the files over the network to them within those constraints.   Thus we get as close to copying the files to the webserver as we can.

I'm not going to explain every property that these ant tasks use.  You'll just have to figure them out on your own sorry but hopefully they are fairly obvious.  I'm also not showing the task that runs the tests or the one that updates our production test webserver in order to run the tests.

This task gets the party started:


 <target name="releaseIfNoProblems">
  <if>
   <equals arg1="${mxunit.error}" arg2="false" />
    <then>
     <if>
      <equals arg1="${mxunit.fail}" arg2="false" />
      <then>
       <antcall target="createTagInSvn" />
       <antcall target="package" />
       <antcall target="pushPackagetoPublicWebsite" />
       <antcall target="sendEmailAboutNewRelease" />
      </then>
     </if>
    </then>
  </if>
 </target>

Next up is the set of steps involved with tagging the release in subversion:

 <target name="readTagFromFile">
  <loadfile property="svn.tag.name" srcFile="application.version.tag.cfm"/>
 </target>

 <target name="createTagInSvn" depends="readTagFromFile">
  <property name="svn.url.tag" value="${svn.url.tags}/${svn.tag.name}" />

  <echo message="source branch: {$svn.url.prod}" />
  <echo message="destionation tag: {$svn.url.tag}" />



   <condition property="svn.tag.exists" value="true" else="false">
          <svnExists javahl="false" svnkit="false" target="${svn.url.tag}" />
      </condition>

  <echo message="svn tag exists: ${svn.tag.exists}" />
  <if>
   <equals arg1="${svn.tag.exists}" arg2="true" />
    <then>
     <!--
      IF we wanted to delete the tag, just in case, before proceeding we could make this block active...
     -->
     <svn username="${svn.username}" password="${svn.password}" javahl="false" svnkit="false" failonerror="false">
      <delete url="${svn.url.tag}" message=" [Build Script] delete previous tag: ${svn.tag.name}" />
     </svn>
     <fail message="The tag ${svn.tag.name} already exists in Subversion.  Please update the application.version.tag.cfm file OR delete the tag in subversion" />
    </then>
    <elseif>
     <equals arg1="${svn.tag.exists}" arg2="false" />
     <then>
      <echo message="the tag doesn't exist yet so let's proceed..." />

      <svn username="${svn.username}" password="${svn.password}" javahl="false" svnkit="false" failonerror="true">
       <copy srcurl="${svn.url.prod}" desturl="${svn.url.tag}" message=" [Build Script] created tag: ${svn.tag.name}" />
      </svn>
     </then>
    </elseif>
  </if>
  <echo message=" ... tagging completed." />
 </target>

Here is the packaging process:
 <target name="prepPackagingArea">
   <delete dir="${package.dir}${ant.project.name}/" />
   <delete file="${package.dir}${package.name}" />
 </target>

 <target name="cleanupPackaging">
   <delete dir="${package.dir}${ant.project.name}/" />
 </target>

 <target name="zipPackage" depends="delDirFromPackage">
  <zip destfile="${package.dir}${package.name}" basedir="${package.checkdir}" />
 </target>

 <target name="delDirFromPackage">
  <delete dir="${package.checkdir}_Database/" />
  <delete dir="${package.checkdir}_DetailImport/" />
  <delete dir="${package.checkdir}_Documentation/" />
  <delete dir="${package.checkdir}uitests/" />
  <delete dir="${package.checkdir}testresults/" />
  <delete dir="${package.checkdir}MVInstall/" />
  <delete dir="${package.checkdir}Replication/" />
  <delete dir="${package.checkdir}styles/" />
  <delete dir="${package.checkdir}unittests/" />
  <delete dir="${package.checkdir}Updater/" />
  <delete>
   <fileset dir="${package.checkdir}" includes="*.xml*" />
   <fileset dir="${package.checkdir}" includes="*.bat" />
   <fileset dir="${package.checkdir}" includes="application.lines.cfm" />
   <fileset dir="${package.checkdir}Config/" includes="config.*" />
  </delete>
 </target>

 <target name="package" depends="prepPackagingArea, readTagFromFile">
  <svn username="${svn.username}" password="${svn.password}" javahl="false" svnkit="false" failonerror="false">
    <export srcurl="${svn.url.tags}/${svn.tag.name}" revision="HEAD" destPath="${package.checkdir}" />
  </svn>
  <antcall target="zipPackage" />

  <antcall target="cleanupPackaging" />
 </target>

Then we need to push the package to the deployment directory and delete the zip file from staging:
 <target name="pushPackagetoPublicWebsite">
  <echo message="pushing the package to an accessible location ${deploy.dir}\${package.name}"/>

  <copy file="${package.dir}${package.name}" tofile="${deploy.dir}\${package.name}" overwrite="true" force="true" />

  <delete dir="${package.dir}" />
 </target>

Finally I need to send out the email(s):
 <target name="sendEmailAboutNewRelease" depends="readTagFromFile">
  <echo message="sending email"/>
  <mail from="XXX" tolist="XXX" mailhost="SOMEHOST" message="Please go to ... and download the zip file for deployment.  Remember, this doesn't include the production config.xml file so you need to preserve the config file at ${ant.project.name}\config\config.xml" subject="${ant.project.name} has a new release ready for installation: version ${svn.tag.name}" />
 </target>
As you might imagine I had to add some extra libraries to ANT. First was the svnant library and then I also had to add ant contrib and finally two jar files from oracle in order to get the mail stuff to work mail.jar and activation.jar

Thursday, January 24, 2013

CF 8, CF9, and CF 10 all on the same apache server!

Today I had the seemingly unfortunate task of adding CF10 to my webserver while continuing to keep CF 8 and CF 9 running on it.  Before you install CF 10 first follow my prior post instructions on getting CF 8 and 9 working together with Apache.

Once you have that working you just have to install CF10 - I installed to c:\dev\tools\ColdFusion10.  I told it to automatically connect to my apache server.  Then, once it was done I had to do a couple tweaks.

First off, I removed the following lines from my httpd.conf:

        #JRunConfig Errorurl url 
        #JRunConfig ProxyRetryInterval 600 
        #JRunConfig ConnectTimeout 15 
        #JRunConfig RecvTimeout 300 
        #JRunConfig SendTimeout 15 


        JRunConfig Apialloc false
        JRunConfig Serverstore "C:/dev/tools/JRun4/lib/wsconfig/cf9/jrunserver.store"
        JRunConfig Bootstrap 127.0.0.1:51000
        AddHandler jrun-handler .jsp .jws .cfm .cfml .cfc .cfr .cfswf

If I left those in there CF got a little confused my CF 10 stuff wouldn't work properly. So take those out. Also, note, to get all of this to work you pretty much have to be using Virtual Hosts. Thus, if you had a virtual host already defined that was talking to CF 9 then you'll need to add these four lines into those virtual hosts:

        JRunConfig Apialloc false
        JRunConfig Serverstore "C:/dev/tools/JRun4/lib/wsconfig/cf9/jrunserver.store"
        JRunConfig Bootstrap 127.0.0.1:51000
        AddHandler jrun-handler .jsp .jws .cfm .cfml .cfc .cfr .cfswf

Next up, create a virtual host that will be using CF 10. You don't have to use a different port; you can still use port 80.

Here is an example virtual host that uses CF 10 and exposes the CF 10 administrator:

#wcs_10
<VirtualHost 127.0.0.1:80>
    DocumentRoot "c:/dev/websites/cften/site/"
    ServerAdmin www.cften.com
    ServerName cften
    ServerAlias *.cften
    ErrorLog c:/dev/websites/cften/log/server.log
DirectoryIndex index.html index.shtml index.php default.htm default.html index.cfm


    <Directory "c:/dev/websites/cften/site">
DirectoryIndex index.html index.shtml index.php default.htm default.html index.cfm
          Options ExecCGI FollowSymLinks
          AllowOverride all
          Allow from all
          Order allow,deny
    </Directory>

        Alias /mxunit /dev/tools/mxunit
        <Directory /dev/tools/mxunit>
        Order allow,deny
        Allow from all
        </Directory>

    # Where to find workers.properties
    JkMountFile "C:\dev\tools\ColdFusion10\config\wsconfig\1\uriworkermap.properties"
    # Select the timestamp log format
    JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
    AddHandler jakarta-servlet .cfm .cfml .cfc .cfr .cfswf

    Alias /CFIDE "C:\dev\tools\ColdFusion10\cfusion\wwwroot\CFIDE"
    <Directory  "C:\dev\tools\ColdFusion10\cfusion\wwwroot\CFIDE">
      Options Indexes FollowSymLinks
      AllowOverride None
      Order allow,deny
      Allow from all
    </Directory>

</VirtualHost>

Finally, go to your apache/conf directory and edit the mod_jk.conf file - you're basically removing a bunch of lines so that your file ends up looking like this:

# Load mod_jk module
LoadModule    jk_module  "C:\dev\tools\ColdFusion10\config\wsconfig\1\mod_jk.so"
JkWorkersFile "C:\dev\tools\ColdFusion10\config\wsconfig\1\workers.properties"
# Where to put jk logs
JkLogFile "C:\dev\tools\ColdFusion10\config\wsconfig\1\mod_jk.log"
# Where to put jk shared memory
JkShmFile "C:\dev\tools\ColdFusion10\config\wsconfig\1\jk_shm"
# Set the jk log level [debug/error/info]
JkLogLevel info
<Files ~ ".hbmxml$">
Order allow,deny
Deny from all
</Files>

Then, restart apache and boom! You'll be all set. CF8, CF9, and CF10 all will be working along side each other. To test it you can put a file like this in the root of your different virtual servers and hit them and see what is reported back:

<cfdump var="#server.coldfusion.productversion#">

Hat tip to http://kmaiya.blogspot.in/2012/04/co-existence-of-coldfusion9-and.html for pointing me in the right direction for getting CF10 working.