919 - 926 - 9847

How I use Subversion for Web Development part 2 - SVN2WEB

I tried to get into ANT.  I really did.  I'm hoping to eventually get to a TACFUG meeting where they do an ANT setup tutorial (I think Jim Priest has done those before).  I did discover this nifty little SVN hook script in the meantime, SVN2WEB.  Walk through the installation guide, and come on back.

So how do I use this, and why?  Let's get to the why first.  I loath FTP.  Really, really, really cannot stand it.  Why?  I've been bitten in the arse far too many times to count overwriting files that should not pushed to production.  You also run into issues with multiple developers touching files on the server, and essentially doing their development on the server (instead of locally).  For that reason, I ONLY allow file pushes from the SVN server to production.  That's where this script comes in.

So let's get this set up.  First, read the tutorial on how I use SVN for development.  On our TRUNK folder, we're going to create a new SVN property.

  • Right click anywhere, and choose TortoiseSVN->Repo Browser.
  • Browse to your Test project, and right click the trunk folder and choose Properties.
  •  Create a new property.  Use the name svn2web, and the property value is something like ftp:www.mysite.com:<password>@<username>:<path on server>.  There's also options for local file system copy or SFTP.
  • This should point to your staging/development/test server.
  • Do the same for the PROD_100 branch, but point it at your production server.

So, every time you make a change to TRUNK or PROD, it's automatically reflected in your environments.  As an added plus, you can make just ONE person in charge of PROD, and all merges must go through that single point of entry.
I find it to be a great system.  Everyone on the team develops locally, does acceptance testing on internet staging, and then deploy to production once the acceptance testing is done.

The only real downside?  Small changes can be a bit more time consuming.  You  need to follow the flow of make the change in TRUNK, merge the change into PROD, then commit that change up.  But it does force everyone to follow a procedure, there's no shortcuts, and no code cowboys mucking about with live code.

How I use Subversion for Web Development

Take this with a grain of salt.  I've read over various how-to's, and although this isn't 100% consistent with some of that, it's worked well for me.


I'm not going to document creating a server, and presuppose you've already done this.  I'll show an example using subClipse and an example using Tortoise SVN client.  The other "gotcha" is you'll need two separate copies of your codebase, or you'll need to "switch" your working copy.  I prefer to have a separate codebase, so that's what you'll see here.  I'll also show you a great little SVN hook, svn2web.

First, some client agnostic instructions:

  • Create a new repo, or create a new project within an existing one.  We'll call ours "MySite"
  • Create a branches folder, and a trunk folder within this new project.  Fairly standard stuff here.
  • In the branches folder, create a PROD_100 folder. 

This is we part ways with "the norm".  We're going to be using the PROD folder to hold our current production site code.  In the end, we'll use svn2web to push changes to our production server.  TRUNK is going to hold all of our current (and on going) development.  We'll be pushing TRUNK to our development server.
Toirtoise SVN

  • Check out your new TRUNK, I put mine in c:\cf_dev\projects\mysite_dev.  After creating the folder to hold your project, right click and choose SVN Checkout… and browse to the SVN server where you created the trunk.
  • Check out your new PROD, I put mine in c:\cf_dev\projects\mysite_prod.  After creating the folder to hold your project, right click and choose SVN Checkout… and browse to the SVN server where you created the PROD_100.
  • Create a new text/html/whatever file in c:\cf_dev\projects\mysite_dev.  I made a test.cfm file.
  • Commit this file.  Right click the file, SVN Commit.  Follow through the prompts, give a comment if you wish.  You now have a file sitting in your TRUNK folder.
  • IMPORTANT: Before you can get this change into PROD, you will need to SVN Update on the c:\cf_dev\projects\mysite_prod folder.  Right click this folder, and choose SVN Update.  This will need to be done EVERY SINGLE TIME!
  • Right click the c:\cf_dev\projects\mysite_prod folder, and choose TortoiseSVN-> Merge…  Choose to merge a range of revisions.  On the next screen, point the URL at where your current TRUNK folder is located.  Then, use the show log to choose your revision ranges, and click next.  Leave the defaults, and choose merge.
  •  Provided you have no conflicts, and at this point you shouldn't as it's a new file, it's time to commit the changes.  You won't be able to merge any other files into this working copy until you commit the current changes!  So, go ahead and right click the c:\cf_dev\projects\mysite_prod\test.cfm file and SVN Commit.
  • Congrats!  You've now merged a change into your production environment!

Eclipse/Subclipse

  • I tend create a new eclipse project for production and development.  So go ahead and create a project for production and development, c:\cf_dev\projects\mysite_dev and c:\cf_dev\projects\mysite_prod.
  • Right click the project, and select Team-> Share Project.  Choose SVN, and the URL to your repository.  Follow through the prompts.  Do this for each project, TRUNK and PROD_100
  • Create a new file in the mysite_dev project. 
  • Right-click the project, and team -> Commit… your changes up
  • On your PROD project, right click and Team-> Update to Head.
  • On your PROD project, right click and Team -> Merge.  Choose to Merge a range of revisions.  Chose the URL to your SVN TRUNK.  After clicking next, you get a nice table view of the available ranges of revisions, along with the comments.  When it finishes, it'll display a dialog of changes.
  • IMPORTANT: You MUST update the PROD project with Team-> Update to head… every single time you wish to do a merge.  You also MUST commit pending changes before you can do any further merges.
  • Commit the merged changes with Team-> Commit.
  • Congrats!  You've now merged a change into your production environment!

Now, the caveats. 

The concept the of merging is thus, at least according to the docs.  You're supposed to create a branch to work on from trunk.  After you're done working with this branch, ideally you would then re-integrate with trunk.  We will not do this.  Ever. 

The idea that I go by is trunk is for all current and future dev, and these branches (PROD_100, PROD_200) match major revisions in my deployment.  I realize that "tags" are typically used for releases, however, that makes are more sense to me when you're talking about Java/C++/insert language here.  For web development, I just don't see this as viable.  Add to that the inclusion of the svn2web hooks that I use, it'd be a tremendous pain to create a hook for every single tag that I'd add.  There's times I make 20-50 commits a week across my various client base.

Next post I'll detail svn2web, but for now, this is how I (and my team) leverage SVN to publish content to our websites.

Cool, I'm a Railo case study!

I'd like to thank AJ Mercry for the push to submit my story, Gert Franz and Micha for offering the platform, and the Railo community for helping me sort out issues the last few years.

Without Railo, I'd not be able to have the server offerings that exist today.  That's not a kang on Adobe ColdFusion, merely saying that without open source tools (apache/mysql/eclipse/subversion), I'd have to charge much higher prices, and shove more clients on a server just to break even.

Thank you Railo community, I can't wait for the goodies coming down the pipe!

Link to the case study is here

GoDaddy drops (shared) ColdFusion support

For whatever reason I only saw this yesterday, but it looks like GoDaddy is dropping out of the CF arena.

I would like to help some of these lost souls out. In the next few weeks, I'm going to put together a server offering with a free 30 day trial just for GoDaddy customers that would like to see if Railo will meet their needs. I don't have all the details formulated yet, but it will include 500 Megs storage, 1 domain, email, etc. I don't yet have the capital together to transfer domains to my own services, but re-pointing to my NS servers will get the job done.

If there's any GoDaddy users ready to just jump ship, before I get the promotion together, you can always hit up my contact link.

Gobless you Daemon, and Gobless open source UPDATE

Continuing further down this rabbit hole, it appears that a "webskin" is NOT the same as an "object". Although you can limit the amount of "objects" being cached, there's presently no means to limit the amount of "webskins".

What does that mean?

Basically, object broker is aware of my HTML objects, like my search page. It is NOT keeping track of the various views that object might have, meaning my search page has dynamic variables that changes what's displayed. Since that search page changes based on a set of variables (keyphrase, page number in the result set, etc), it's possible to have more than one webskin cached. In my case, things started going to hell after about 3000 cached pages or 400 megs of memory cache.

As much as I'd like to cache search page results, this particular client isn't going to get that goodness without forking over money for more memory at this point.

It doesn't look like this is high on the priority list for change for Daemon, but I think it'd be beneficial to limit the amount of cached webskins (or size) as well. If I can scrape together enough free time, I'll see if I can pull that off.

FarCry: admin redirect for Railo, and error handler update

So, first item of business. The /farcry/core/webtop folder for FarCry is a real pain to remember, and clients loath having to type that out all the time. To get around this, you're going to need to make the following edits. NOTE: This assumes that you have Apache, rewriting enabled and Railo as your CFML engine. It can be easily adapted to other engines and webservers.

You'll need to open up the webroot/farcryConstructor.cfm file first.


<!--- find this --->
<cfset THIS.webtopURL = "/farcry/core/webtop" />

<!--- change to this --->
<cfset THIS.webtopURL = "/cmsadmin" />

Next, we're going to add an Alias to our web server. You'll need to adjust this if you're using IIS


Alias /cmsadmin "C:\<sitehomes>\<myproject>\farcry\core\webtop"

Our last step is going to be a CFML mapping. In Railo, you visit your http://site/railo-context/admin/web.cfm. Go to the mappings link, and add a resource for /cmsadmin and C:\\\farcry\core\webtop. If it doesn't exist, you'll see a nice red tinge to the box that you entered the path into.

Update your FarCry application, or restart your CFML engine

Bob's your uncle, you should now be able to now browse http://site/cmsadmin and see the FarCry admin!

Second order of business, dealing with the default error handling. It's really great to see what's going when something breaks, it's really BAD for other people to see this in production. It's pretty simple to resolve. The below code will still allow for you to see errors when running test locally, but send all server based errors to email.

Find your webroot/Application.cfc and make the following edit


<!--- find this --->
<cfset super.OnError(argumentCollection=arguments) />

<!--- replace with --->
<cfset var machineName = createObject("java", "java.net.InetAddress").localhost.getHostName() />
        <cfif machinename contains("<server name... use cgi variable to get this>")>
            
            <cfmail from="<valid mail>" to="<valid mail>" subject="ERROR - <sitename>" type="html">
            <cfdump var="#url#">
            <cfdump var="#session#">
            <cfdump var="#arguments#">
            </cfmail>
<!--- send to an error page... need to make this--->
            <cflocation url="/site-error" addtoken="false">
        <cfelse>
            <cfset super.OnError(argumentCollection=arguments) />
        </cfif>    

Gobless you Daemon, and Gobless open source

I've been tracking down a funky memory issue as of late, and it's down to some of the caching being done on one of my FarCry HTML objects. Essentially, the PS Old Gen fills up, and during the course of trying to run a GC on it, one of my four CPU cores just goes AWOL. I'll kill the thread in htop, and the Resin watchdog service will automagically fire back up, and within about 3 minutes we're good to go.

After having something concrete to pass to team Daemon, I'm truly thrilled to have gotten a response back in just a few hours time. Turns out, Daemon has already been working on the objectBroker caching abilities. They now keep a watch on memory use, and automatically start clearing objects out of cache should memory start to go downhill.

I've been watching FusionReactor off and on for the past 3 hours, and I think this fix has nailed the problem. Before, a GC wouldn't bring memory down past 70% or so. Now, it's coming back down to 50%. Combine that with FusionReactor watching for super long running requests, and I think I can now go back to my customer and tell them we can expect no more major slow downs or memory issues. I love open source communities!

If you're looking for this fix, you can find it in both the p610 branch and p600 branch

Might be the newest Railo case study

May wonders never cease. AJ Mercer and I were having a chat about FarCry, and the conversation turned to Railo hosting. I did catch the post from Sean about needing more marketing by us (the people in the trenches), but didn't really think myself a candidate for this. But, AJ pointed out it'd make for a good case study.

So, I've submitted my information to Gert, and I guess we'll see if it shows up on the case study page. I haven't look over the sites we host in awhile, but it surprised me to see it's up to 30+ as of now. It truly wouldn't be possible, at least on a VPS plan such as those that linode.com offers, without a great product like Railo. Way to go, team Railo, I eagerly await Railo 4.0!

Thank you team Intergral!

As mentioned on the last post, I used FusionReactor to help with tracking down memory issues. I'm not new to the FR boat, as we've used it on my day job for several years to help keep the various CF servers operating under large amounts of load. I've not really dealt with support, beyond requesting activation help, before this past week.

However, that all changed this week. I run Railo/Resin and Apache on all of my current hosted projects, the main reason being Railo is pretty darn fast, and at the right price. Yes, I get that Resin is pretty out dated, but I've not had time to rip apart the current setup to convert to something like Tomcat.

For some reason, when you use the hosts folder option for Resin, contexts constantly appear to restart to FusionReactor. After some back and forth with the Intergral team, they ended up providing a JAR file that resolved this. But the thing to take away here is I'm not yet a paying customer, and they've responded to my requests starting at 7AM EST, and as late as 9:30PM EST at night! I'm waiting on the customer to go ahead with the purchase amount, but team Intergral is a win in my book!

Railo scope dumper

I'm presently trying to run down a memory (perceived) leak issue on an application that I've hosted for 3 years. In the past 3 months, this app has moved to the FarCry framework from a... less than stellar homebrew solution

At first, I thought it was Lucene shorting out, but I didn't have anything concrete beyond the amount of traffic hitting it. I installed FusionReactor, and although it's very helpful to watch the graphs, it still didn't give all that much accuracy into what was going on. Until...

A heap dump from yesterday revealed that railo.runtime.type.scope.ApplicationImpl had grown to 450 megs for just one site (the one converted recently). After asking around the Railo list, this object appears to be the application scope. Further suggestion was to loop over the keys, and use the built-in sizeOf and countOf functions to see what's going on (yay Railo!).

With some manual dumping of those keys, it appears that the cached HTML object types had grown far beyond expected. Way, far beyond. Ooops. So I cleared the cached objects, and poof, memory was manageable again.

So, on to a handy little file/CFC that will dump out scopes in Railo (I don't think these functions exist in Adobe CF, but I've not tested yet). Much thanks to Gert Franz, Michael Offner, Jay, and Peter Boughton (especially for those key loops Peter!)

First, you need this template. scopeDump.cfm


<!--- instantiate scope object --->
<cfset oGetScopeDetail=createObject("component", "scopeDump")>
<cfoutput>
<form action="" method="post">
<!--- set what scopes you want to dump --->
<select name="scope" id="scope">
    <option value="application">Application</option>
    <option value="session">Session</option>    
    <option value="cgi">CGI</option>
</select>

<input id="submit" name="submit" type="submit" value="GO!">
<cfif structkeyExists(form,"submit")>

<!--- You MUST get the struct to pass into the function --->
    <cfset stScope=oGetScopeDetail.getDetail(structGet(form.scope))>
    <cfif structkeyExists(stScope,'countOf')>
        <p>Count of #form.scope#: #stScope.countOf#</p>
    </cfif>
    <cfif structkeyExists(stScope,'sizeOf')>
        <p>Size of #form.scope#: #stScope.sizeOf#</p>
    </cfif>
<!--- Looping over the returned objects in the structure --->
    <cfif structkeyExists(stScope,'aItems')>
        <table border="1">
            <tr>
                <td>Item</td>
                <td>Count</td>
                <td>Size</td>
            </tr>
            <cfloop from="1" to="#arraylen(stScope.aItems)#" index="i">
                <tr>
                    <td>#stScope.aItems[i].name#</td>
                    <td>#stScope.aItems[i].countOf#</td>
                    <td>#stScope.aItems[i].sizeOf#</td>
                </tr>
            </cfloop>
        </table>
    </cfif>
</cfif>
</form>
</cfoutput>

You'll also need this little CFC, scopeDump.cfc


<cfcomponent displayname="scopeDump" >

    <cffunction name="getDetail" access="remote" returntype="struct">
        <cfargument name="scopeVar" required="yes" type="struct">
        
        <cfset var rStruct=structnew()>
        <cfset rStruct.countOf=numberformat(StructCount(arguments.scopeVar),',')>
        <cfset rStruct.sizeOf=numberformat(SizeOf(arguments.scopeVar),',')>
        <cfset var aItems=arraynew(1)>
        <cfset var stItems=structnew()>
        <cfset var tmp="">
        <cfloop item="CurItem" collection=#arguments.scopeVar#>
            <cfset stItems.name=CurItem>
            <cfif isStruct(arguments.scopeVar[CurItem])>
                <cfset stItems.countOf=numberformat(StructCount(arguments.scopeVar[CurItem]),',')>
            <cfelse>
                <cfset stItems.countOf=0>
            </cfif>
            <cfset stItems.sizeOf=numberformat(SizeOf(arguments.scopeVar[CurItem]),',')>
            <cfset tmp=arrayappend(aItems,structcopy(stItems))>
            <cfset tmp=structclear(stItems)>
        </cfloop>
        <cfset rStruct.aItems=aItems>

        
        <cfreturn rStruct>
    </cffunction>
</cfcomponent>

More Entries