Pragmatic Automation http://pragmaticautomation.com/cgi-bin/pragauto.cgi Pragmatic Automation. Hosted by Mike Clark. en-us Rapid Feedback http://pragmaticautomation.com/cgi-bin/pragauto.cgi/Deploy/RapidFeedback.rdoc <a href="http://x180.net">James Duncan Davidson</a> and I are building a Rails application for a client. We needed a way to synchronize the code in the Subversion repository with the code being run on the shared test server. That way, everyone on the team could benefit from rapid feedback. <p> Time for a little automation. So James cooked up a Subversion post-commit script which I&#8217;ve posted on my <a href="http://www.clarkware.com/cgi/blosxom/2005/05/22#RapidFeedback">blog</a>. </p> Tell Me Where It Hurts http://pragmaticautomation.com/cgi-bin/pragauto.cgi/Deploy/TellMeWhereItHurts.rdoc <dl> <dt></dt><dd><em>Minutes ago, you released the latest version of your buzzword-compliant hosted application to your most important customer. It&#8217;s no small accomplishment for the team and is a potentially huge deal for your company. Your face lights up with a big, toothy grin as a wave of high-fives goes around the team. When the excitement turns into a collective sigh of relief, the team huddles up to depart for a well-deserved celebration dinner. Just then, the sound of a ringing telephone cuts through the air, freezing everyone in their tracks. It&#8217;s the support phone, and that could only mean one thing: deployment trouble.</em> </dd> <dt></dt><dd>(<a href="http://www.pragmaticprogrammer.com/starter_kit/auto/DiagnosticTests.pdf">Full article: PDF</a>) </dd> </dl> Ah yes, if you haven&#8217;t been there then count yourself lucky. Getting an application properly seated in its production environment, time after time, requires careful planning and inspection. The slightest misconfiguration can tie up endless hours troubleshooting and quickly rack up cell phone minutes. <p> Read how to write and distribute diagnostic tests that let your application do it&#8217;s own first-level support in my recent <a href="http://www.pragmaticprogrammer.com/starter_kit/auto/DiagnosticTests.pdf">article (pdf)</a> for <a href="http://www.stickyminds.com/BetterSoftware">Better Software magazine</a>. </p> Automation Saves Christmas http://pragmaticautomation.com/cgi-bin/pragauto.cgi/Deploy/AutomationSavesChristmas.rdoc <em>Last Christmas, Simon Chappell and his team leaned on software automation to keep up with the pace of warehouse automation. Santa only wishes his elves were this resourceful. Simon writes:</em> <p> Last year, Lands&#8217; End selected its strategic J2EE application server and the word came down from upon high that projects should port to it as quickly as possible. Because life has a sense of humour and I&#8217;m often the punchline, it was decided that our project should be the first one to make the transition. </p> <p> Being a big and expensive application server (no names to protect the guilty), it lives on its own server and mere programmers are not allowed to touch it. This eliminated the easy deploy that we had previously enjoyed. But the spirit of pragmatism will not be deprived! Now our Ant script has a target for building the required EAR file and then we run a second script to use secure copy to move the file to a directory on a designated machine. </p> <p> For our web application, we had special capacity needs above and beyond the capabilities of the internal toolset. While our public facing website is very well tested, it is done using an external service. This was not an option for our internal web application. Yet, the load it was required to handle was above anything that we could generate with even our whole team rapidly hitting it from our workstations. By using <a href="http://clarkware.com/software/JUnitPerf.html">JUnitPerf</a>, I was able to construct a test that simulated anything from 1 to 100 simultaneous users, using <a href="http://httpunit.sourceforge.net">HttpUnit</a> to interact with our web application and with each simulated user using unique data. </p> <p> With the realistic traffic that we generated, we were able to identify and resolve several bottlenecks in our system&#8217;s performance. The day was saved. Our system took us through our Christmas peak season with no support calls. And with the tests being scripted, we can re-run them at anytime that we please: after changes to the internal infrastructure, or as we recently did to test that our system would run unaffected by our newest companion system in the J2EE cluster. </p> <p> If you can&#8217;t automate everything, you automate what you can. </p> <p> <em> I was teaching at Lands&#8217; End earlier this month and got to see Simon&#8217;s web application being put to the task during this year&#8217;s peak shopping season. At stations on the warehouse floor, amidst a dizzying network of conveyor belts loaded with holiday gifts, an army of workers use the web application to help package and ship orders. It seemed only fitting that delivering the software to these work stations be as automated as delivering the packages to your house. Seeing this well-oiled machine in action, I just kept repeating one word: Wow! </em> </p> One Build File, Many Deployment Environments http://pragmaticautomation.com/cgi-bin/pragauto.cgi/Deploy/ManyDeploymentEnvironments.rdoc <em>Daniel Frey sent in this report describing how he uses Ant&#8217;s immutable properties and code generation to build a J2EE application for any given deployment environment at the push of a button:</em> <p> I was looking for a way to manage automation for a web-based, J2EE system deployed into development, QA, pre-production, and production environments, each of which uses a WebLogic application server and an Oracle database. My biggest concern was the configuration data for the database and application server. A &quot;push button&quot; build would only be possible for one environment without some way to parameterize the configuration data. Ant has facilities to aid in this, so I expanded on what I read in <a href="http://www.pragmaticprogrammer.com/sk/auto/">Pragmatic Project Automation</a> to help me achieve this cross-environment deployment automation. </p> <p> Before describing how I created my &quot;push button&quot; build, let me briefly explain our system. It&#8217;s a J2EE-based application in a tiered architecture. Persistence for the proof of concept was achieved using a <a href="http://www.mysql.com/">MySQL</a> database. <a href="http://www.hibernate.org/">Hibernate</a> is providing the O/R mapping capabilities and the <a href="http://www.springframework.org/">Spring</a> framework is wiring all of this up together. Hibernate provides Ant tasks for generating <tt>.java</tt> files and building the database structure from Hibernate XML files. I am also using <a href="http://dbunit.sourceforge.net/">DBUnit</a> as a means of populating test data. </p> <p> I created my project directory structure as defined in <a href="http://www.pragmaticprogrammer.com/sk/auto/">Pragmatic Project Automation</a> with a couple of minor additions. First, I added a <tt>config</tt> directory to the root of my project directory structure. The <tt>config</tt> directory contains various configuration files&#8212; properties files and XML files&#8212;for the deployment environment. These configuration files contain place holders (tokens) for deployment information such as what database to use. The tokens are replaced with actual values when Ant copies the configuration files to the <tt>build/prod</tt> directory. Here is the <tt>config</tt> Ant target that performs the copy: </p> <pre> &lt;target name=&quot;config&quot; depends=&quot;prepare&quot;&gt; &lt;copy todir=&quot;${build.prod.dir}&quot;&gt; &lt;fileset dir=&quot;${config.dir}&quot;&gt; &lt;include name=&quot;*.properties&quot; /&gt; &lt;include name=&quot;*.xml&quot; /&gt; &lt;/fileset&gt; &lt;filterset begintoken=&quot;%&quot; endtoken=&quot;%&quot;&gt; &lt;filter token=&quot;HIBERNATE.DIALECT&quot; value=&quot;${hibernate.dialect}&quot; /&gt; &lt;filter token=&quot;HIBERNATE.DRIVER&quot; value=&quot;${hibernate.connection.driver_class}&quot; /&gt; &lt;filter token=&quot;HIBERNATE.URL&quot; value=&quot;${hibernate.connection.url}&quot; /&gt; &lt;filter token=&quot;HIBERNATE.USER&quot; value=&quot;${hibernate.connection.username}&quot; /&gt; &lt;filter token=&quot;HIBERNATE.PASSWORD&quot; value=&quot;${hibernate.connection.password}&quot; /&gt; &lt;/filterset&gt; &lt;/copy&gt; &lt;/target&gt; </pre> <p> This copies all of the configuration files in the <tt>config</tt> directory to the <tt>build/prod</tt> directory. As the files are copied, the <tt>filterset</tt> replaces all of the tokens observed in the configuration files with the appropriate values. All of the token values are supplied in one properties file that I specify when Ant runs the build file. For example, when I want to deploy into an environment that uses MySQL, I use a properties file that contains these key/value pairs: </p> <pre> hibernate.dialect=net.sf.hibernate.dialect.MySQLDialect hibernate.connection.driver_class=com.mysql.jdbc.Driver hibernate.connection.url=jdbc:mysql://server/database hibernate.connection.username=user hibernate.connection.password=password </pre> <p> Using this file, any configuration file in the <tt>config</tt> directory that contains the token <tt>%HIBERNATE_USER%</tt>, for example, will be changed to reference the value <tt>user</tt>. I have a default properties file, but I can change that via the command line when Ant is run. For example, if I want to deploy to the QA environment, I use: </p> <pre> $ ant -propertyfile qa.properties </pre> <p> I can also override specific properties on the command line. For example, to override the value of <tt>hibernate.connection.username</tt>, I&#8217;d type: </p> <pre> $ ant -Dhibernate.connection.username=admin -propertyfile qa.properties </pre> <p> I also added a <tt>stage</tt> directory to my <tt>build</tt> directory to store the <tt>.java</tt> files generated by Hibernate. I needed a separate staging area for these files because I did not want them to be in the <tt>src</tt> directory. These are generated files and can be re-generated at any given time by executing the <tt>codegen</tt> target: </p> <pre> &lt;target name=&quot;codegen&quot; depends=&quot;config&quot;&gt; &lt;taskdef name=&quot;hbm2java&quot; classname=&quot;net.sf.hibernate.tool.hbm2java.Hbm2JavaTask&quot; classpathref=&quot;project.classpath&quot; /&gt; &lt;hbm2java output=&quot;${build.stage.dir}&quot;&gt; &lt;fileset dir=&quot;${src.dir}&quot;&gt; &lt;include name=&quot;**/*.hbm.xml&quot; /&gt; &lt;/fileset&gt; &lt;/hbm2java&gt; &lt;copy todir=&quot;${build.prod.dir}&quot;&gt; &lt;fileset dir=&quot;${src.dir}&quot;&gt; &lt;include name=&quot;**/*.hbm.xml&quot; /&gt; &lt;/fileset&gt; &lt;/copy&gt; &lt;/target&gt; </pre> <p> The <tt>hbm2java</tt> task generates the <tt>.java</tt> files into the <tt>build/stage</tt> directory from the Hibernate <tt>.hbm.xml</tt> files in the <tt>src</tt> directory. The <tt>&lt;copy&gt;</tt> task just moves the <tt>.hbm.xml</tt> files to the <tt>build/prod</tt> directory so that they are available on the classpath for the Spring framework to find. </p> <p> After all the configuration is complete and the <tt>.java</tt> files are generated, Ant then compiles everything: </p> <pre> &lt;target name=&quot;compile&quot; depends=&quot;codegen&quot;&gt; &lt;javac srcdir=&quot;${build.stage.dir}&quot; destdir=&quot;${build.prod.dir}&quot;&gt; &lt;classpath refid=&quot;project.classpath&quot; /&gt; &lt;/javac&gt; &lt;javac srcdir=&quot;${src.dir}&quot; destdir=&quot;${build.prod.dir}&quot;&gt; &lt;classpath refid=&quot;project.classpath&quot; /&gt; &lt;/javac&gt; &lt;/target&gt; </pre> <p> However, I don&#8217;t run my tests right away. Hibernate must first generate and execute the DDL to the MySQL database in order to set up the database structure: </p> <pre> &lt;target name=&quot;schema&quot; depends=&quot;compile&quot;&gt; &lt;taskdef name=&quot;schemaexport&quot; classname=&quot;net.sf.hibernate.tool.hbm2ddl.SchemaExportTask&quot; classpathref=&quot;project.classpath&quot; /&gt; &lt;schemaexport properties=&quot;${build.prod.dir}/hibernate.properties&quot; quiet=&quot;no&quot; text=&quot;no&quot; drop=&quot;no&quot; delimiter=&quot;;&quot;&gt; &lt;fileset dir=&quot;${build.prod.dir}&quot;&gt; &lt;include name=&quot;**/*.hbm.xml&quot; /&gt; &lt;/fileset&gt; &lt;/schemaexport&gt; &lt;/target&gt; </pre> <p> Then I use DBUnit to populate my test data: </p> <pre> &lt;target name=&quot;setupDB&quot; depends=&quot;schema&quot;&gt; &lt;taskdef name=&quot;dbunit&quot; classname=&quot;org.dbunit.ant.DbUnitTask&quot;&gt; &lt;classpath refid=&quot;project.classpath&quot; /&gt; &lt;/taskdef&gt; &lt;dbunit driver=&quot;${hibernate.connection.driver_class}&quot; url=&quot;${hibernate.connection.url}&quot; userid=&quot;${hibernate.connection.username}&quot; password=&quot;${hibernate.connection.password}&quot;&gt; &lt;operation type=&quot;CLEAN_INSERT&quot; src=&quot;db/TestData.xml&quot; /&gt; &lt;/dbunit&gt; &lt;/target&gt; </pre> <p> Finally, Ant runs the <tt>test</tt> target to compile and execute my tests. With these build targets chained together as I&#8217;ve described, I can execute the <tt>test</tt> target and everything is run at the push of a button. </p> <p> This has proved to be very useful and successful every time I execute the build script. It also provides a number of advantages. First, I can execute this on each of my systems, changing only the properties file referenced on the command line. Second, I can easily change the target database because I am using Hibernate to provide O/R mapping capabilities. To accomplish this, all I have to do is change the value of the Hibernate properties in one properties file or on the command line when Ant is run. I have successfully done this between MySQL and HSQLDB. </p> YIGNI http://pragmaticautomation.com/cgi-bin/pragauto.cgi/Deploy/YIGNI.rdoc <a href="http://www.vanderburg.org/Blog">Glenn Vanderburg</a> shares this story from the field about why you should always assume that you&#8217;ll need a real programming language if you want to allow your software system to be automated through commands, and how doing so can make your system more robust: <p> <em>In 1999 I was consulting for a company that builds telecom equipment. They were developing a new variety of &#8230; well, let&#8217;s just call it a &quot;local area telecom network.&quot; It included multiple nodes for traffic switching, call control and setup, and administration. It was to be a full-fledged product, but (as so often happens) there was an artificially imposed deadline: they wanted to demonstrate the product at a trade show in just a few months. Those kinds of things can be death to a project, but this company was attacking it rationally&#8212;for the demo, they were quickly pulling together &quot;version 1&quot; using existing internal simulation, testing, and prototyping tools, while a different team was working on building &quot;version 1.5&quot; from scratch, and there was a reasonable amount of communication and feedback between the two teams.</em> </p> <p> <em>I was working on the operations and management system for the version 1 team, and was asked to design and build a system that could start and configure the various separate processes on a node, hooking them together so that they would be ready to join the network and start doing work. The testing group had an existing tool that did a similar job, and although it wasn&#8217;t suitable for use even in the demo product, the team asked that I keep the command structure of that testing tool. I don&#8217;t remember the details, but that tool could respond to three or four very simple commands: &quot;connect,&quot; &quot;send,&quot; &quot;wait,&quot; for example.</em> </p> <p> <em>At that time I was a fan of the Tcl language, which was designed to be a neutral, extensible, embedded command language for applications. Tcl&#8217;s inventor, John Ousterhout, designed the language after noticing a pattern in application development: nearly every application needs a command language of some sort, but the developers are focused on the application itself, so they cut corners with the command language, thinking they can get by with less. The command languages end up having &quot;insufficient power and clumsy syntax,&quot; in Ousterhout&#8217;s words &#8230; and then, contrary to the developers&#8217; expectations, users find that they need to use that command language to do complex things.</em> </p> <p> <em>I saw this pattern developing in our telecom system. The command language I was asked to develop was almost hopelessly simple, but the people asking for it were adamant that it was all they needed. And yet I noticed that the command syntax was completely compatible with Tcl syntax. Plus, an implementation of Tcl atop Java, called <a href="http://tcljava.sourceforge.net/docs/website/index.html">Jacl</a>, was available for me to use. Because Jacl, like Tcl, was designed to be extended with application-specific commands, I made the case that it would be just as easy&#8212;probably easier, in fact&#8212;for me to implement the desired commands as Jacl extensions than to write a custom parser. Then they would have a real programming language at their disposal &quot;in the unlikely event that they ever needed it.&quot; My clients reluctantly agreed to this plan, but only because it would not involve extra cost. They were sure they knew their needs.</em> </p> <p> <em>Implementing the commands as Jacl extensions <b>was</b> easy, and the payoff was huge. During preparations for the first deployments (for the trade show demo and a couple of early customers) the team wrote hundreds of lines of Tcl scripts to automate the setup and configuration of the different pieces of software that made up the network. If that power hadn&#8217;t been available to them, they would&#8217;ve had to make requests to the development team for added features in the system, which would&#8217;ve been a huge bottleneck. It was generally acknowledged that the use of Tcl for the configuration language was one of several decisions that helped the deployment teams meet the project deadlines. It also made the system more robust: the configuration scripts were originally intended just for starting the system, but they turned out to be very useful for dynamic error detection and recovery as well. Tcl has fallen out of favor (for reasons both good and bad) and today I&#8217;d probably use Ruby or Python. But that&#8217;s an implementation detail.</em> </p> <p> <em>I&#8217;m a big believer in the YAGNI principle: if you don&#8217;t have a real, demonstrated need for some architectural or design feature today, assume that &quot;you ain&#8217;t gonna need it&quot; and keep your design simple and focused on the needs of the day. But YAGNI is a principle, not a law, and there are some places where I&#8217;ve learned that it doesn&#8217;t apply. This is one: if you know you need a scripting, configuration, or extension language&#8212;something that allows your system to be automated&#8212;you can safely assume that you need a complete programming language in that role. YIGNI: you <b>is</b> gonna need it.</em> </p> <p> I find dynamic languages such as Ruby and Python to be ideal for this type of commanded automation, though they certainly aren&#8217;t limited to this scope. Glenn&#8217;s story also reminds me how convenient it is that most of my GUI-based Mac applications can be automated using AppleScript. I wish more applications had this type of commanded automation interface. </p>