<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-734195773111742577</id><updated>2011-09-05T05:36:17.251-07:00</updated><title type='text'>Jason Chambers</title><subtitle type='html'>Ramblings on software development process and architecture</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>22</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-8313969371332657279</id><published>2010-12-05T11:38:00.001-08:00</published><updated>2010-12-05T11:42:46.256-08:00</updated><title type='text'>Introduction to Gradle</title><content type='html'>At my new gig, one of my first assignments was to re-engineer our build system. For this task I used Gradle. To ease the transition for the team, I put together a YouTube video&lt;br /&gt;&lt;br /&gt;&lt;iframe width="425" height="344" src="http://www.youtube.com/embed/TrLnMs89P5E?fs=1" frameborder="0"&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-8313969371332657279?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/8313969371332657279/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=8313969371332657279' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/8313969371332657279'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/8313969371332657279'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2010/12/introduction-to-gradle.html' title='Introduction to Gradle'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/TrLnMs89P5E/default.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-243674690628710871</id><published>2010-09-29T06:57:00.000-07:00</published><updated>2010-09-29T06:58:38.493-07:00</updated><title type='text'>Tips on designing an upgrade process</title><content type='html'>I’d like to share some of my lessons learned and pass these on.&lt;br /&gt;Consider the current working version (x) sacred – the upgrade process should not touch it because you always need to provide an option to roll-back to (x) – perhaps even a couple of hours after the new version (y) has been deployed. Design hint: when upgrading, lay the new version (y) along-side and not on top of version (x). This implies a migration of configuration files at the application layer and data migration at the data layer from version (x) to version (y).&lt;br /&gt;&lt;p&gt;&lt;br /&gt;In a high-availability environment, consider is it possible to run both version (x) and (y) concurrently – this is to support the concept of a rolling upgrade where you upgrade each node in turn.&lt;br /&gt;&lt;p&gt;&lt;br /&gt;By far, the trickiest part of designing an upgrade process is considering evolutionary changes to the data schema. New tables, columns or constraints may have been added. You need to pay close attention to changes to the schema during development and always think about what this means for the migration of existing data. It’s usually a good idea to embed the version number in the name of the schema/database.&lt;br /&gt;&lt;p&gt;&lt;br /&gt;It’s essential that data created with the old version (x) is available with the new version (y) – but also, ensure that the operations the system provides on the data exhibit a consistent behavior where expected.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-243674690628710871?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/243674690628710871/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=243674690628710871' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/243674690628710871'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/243674690628710871'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2010/09/tips-on-designing-upgrade-process.html' title='Tips on designing an upgrade process'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-5598749448029943654</id><published>2010-09-22T11:11:00.000-07:00</published><updated>2010-09-22T11:45:00.110-07:00</updated><title type='text'>DevNexus presentation</title><content type='html'>Back in March of 2010, I gave a presentation at the &lt;a href="http://devnexus.com"&gt;devnexus.com&lt;/a&gt; conference in Atlanta.&lt;br /&gt;&lt;br /&gt;The presentation was entitled "From whiteboard to product launch". The intent being to share my teams experience in bringing a brand-new product to the marketplace. It covers a wide variety of areas including process, architecture and team organization.&lt;br /&gt;&lt;br /&gt;The slides are available &lt;a href="http://www.devnexus.com/static/2010/slides/DevNexus2010-Jason%20Chambers-From%20whiteboard%20to%20product%20launch.pdf "&gt;here&lt;/a&gt; and the audio is available &lt;a href="http://www.devnexus.com/static/podcast/DevNexus_Podcast_Episode_1-Jason_Chambers-From_Whiteboard_To_Product_Launch_(DevNexus_2010).mp3 "&gt;here&lt;/a&gt; (I recommend you flip through the slides while listening to the audio).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-5598749448029943654?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/5598749448029943654/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=5598749448029943654' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/5598749448029943654'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/5598749448029943654'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2010/09/devnexus-presentation.html' title='DevNexus presentation'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-340628289293184545</id><published>2008-12-29T08:54:00.000-08:00</published><updated>2010-09-22T13:49:03.614-07:00</updated><title type='text'>How to scale the data layer</title><content type='html'>&lt;div&gt;Many a performance problem as a system struggles to support growing demand can be pointed to the data layer. The database can easily become a bottleneck and can be the hardest to scale after the fact because it manages state (in contrast, stateless components are easy to scale).&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;div&gt;Of course, the problem may be solved by throwing more hardware at the problem – i.e. upgrading the database server by increasing memory and/or CPU horsepower. However, it is prudent to think about scalability before rather than after the fact.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;First, let us consider some elementary physics:&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;em&gt;pressure = force / area&lt;/em&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href="http://4.bp.blogspot.com/_SIJ9fKIpUKU/SVkVZjNSY3I/AAAAAAAAABI/yFQRv8n_zBc/s1600-h/ballerina.PNG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5285279166387872626" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 316px; CURSOR: hand; HEIGHT: 212px" alt="" src="http://4.bp.blogspot.com/_SIJ9fKIpUKU/SVkVZjNSY3I/AAAAAAAAABI/yFQRv8n_zBc/s320/ballerina.PNG" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;For a given force, if we reduce the area to which&lt;a href="http://1.bp.blogspot.com/_SIJ9fKIpUKU/SVkVI4Sa_BI/AAAAAAAAABA/fNJ_yIRsE44/s1600-h/ballerina.PNG"&gt;&lt;/a&gt; the force is applied the pressure increases. Try for example, standing on tip-toes like a ballerina to get a sense of what I’m talking about. The force i.e. your body weight is constant, but we are reducing the surface area to which the force is applied resulting in increased pressure. &lt;/div&gt;&lt;br /&gt;&lt;div&gt;Likewise, if we increase the surface area the pressure is reduced. This is how a person can lay on a bed of nails without puncturing the skin – there needs to be of course enough nails.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;a href="http://1.bp.blogspot.com/_SIJ9fKIpUKU/SVkW36mWlwI/AAAAAAAAABQ/FzwOK5bCJXI/s1600-h/bedofnails.PNG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5285280787574724354" style="FLOAT: left; MARGIN: 0px 10px 10px 0px; WIDTH: 274px; CURSOR: hand; HEIGHT: 286px" alt="" src="http://1.bp.blogspot.com/_SIJ9fKIpUKU/SVkW36mWlwI/AAAAAAAAABQ/FzwOK5bCJXI/s320/bedofnails.PNG" border="0" /&gt;&lt;/a&gt;Typically, we don’t have any real control over the forces applied to the products or solutions we are designing and building. We can speculate and design with certain limits in mind, however what if we come across a situation where those limits are shattered? In the design, we need to think of a way of how we can support this and yet reduce the pressure on the entire system.&lt;br /&gt;&lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt;One approach is to use horizontal and vertical portioning techniques in the design of the data layer. Both these approaches have the effect of increasing the surface area and thus reducing pressure points as the force (or load) on the system increases compared to a single monolithic data layer (i.e. single database server).&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Horizontal partitioning is where the rows of a single logical table are spread over multiple physical databases. For example, customers whose last name begins with A-F may be stored in one database server and customers whose last name begins with G-L are stored in another database server and so on. It is a popular technique used by several large dot-coms as a way to spread the load.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Vertical portioning is a similar concept but involves storing different tables in different physical databases. For example, purchase orders may be stored in one database server whereas invoices may be stored in another.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;To learn more about horizontal and vertical partitioning, take a look at &lt;a href="http://en.wikipedia.org/wiki/Partition_(database)"&gt;http://en.wikipedia.org/wiki/Partition_(database)&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Conceptually, the approaches are very straightforward however if you want to leverage them in your design there are several things to look out for.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;For horizontal paritioning, you want to strive for even distribution across the partitions. For example, partitioning based on the first letter of the last name may not provide even distribution - V through Z for example may be quite light in the number of records. Hashing techniques may provide a good approach.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;For vertical partitioning, references across data models may be tricky. In particular, forget about foreign key constraints. If the two data models have a high degree of coupling between them, then I would go back to the drawing board – perhaps you haven’t found the right boundary. It’s ok to go to one data model, find what you are looking for, then use the result from that query, to find what you ultimately need from the second data model. Avoid distributed transactions (XA/2PC) that span more than one data model if at all possible. Ask yourself, do you really need referential integrity across two data models at all times? If so, then again you should revisit your data design and carve out the right boundaries.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;When designing data models at the highest level, strive for high-cohesion (if it changes together, then it stays together) and loose-coupling between the data models. In other words, apply some of the same desirable properties for code design to your data design. &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-340628289293184545?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/340628289293184545/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=340628289293184545' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/340628289293184545'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/340628289293184545'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/12/how-to-scale-data-layer.html' title='How to scale the data layer'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_SIJ9fKIpUKU/SVkVZjNSY3I/AAAAAAAAABI/yFQRv8n_zBc/s72-c/ballerina.PNG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-4359641331120176731</id><published>2008-12-08T11:49:00.001-08:00</published><updated>2008-12-08T11:51:30.171-08:00</updated><title type='text'>You know you're a geek when</title><content type='html'>Writing a shopping list the other day, I wrote down &lt;a href="http://code.google.com/p/google-guice/"&gt;Guice&lt;/a&gt; instead of Juice. Time for a break from work I think.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-4359641331120176731?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/4359641331120176731/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=4359641331120176731' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/4359641331120176731'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/4359641331120176731'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/12/you-know-youre-geek-when.html' title='You know you&apos;re a geek when'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-2443832722805055214</id><published>2008-12-04T07:55:00.001-08:00</published><updated>2008-12-04T07:55:35.302-08:00</updated><title type='text'>Tomcat Controller update</title><content type='html'>&lt;a href="http://www.jasondchambers.com/2008/07/tomcat-controller.html"&gt;http://www.jasondchambers.com/2008/07/tomcat-controller.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-2443832722805055214?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/2443832722805055214/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=2443832722805055214' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/2443832722805055214'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/2443832722805055214'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/12/tomcat-controller-update.html' title='Tomcat Controller update'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-8015451593665060560</id><published>2008-11-12T15:31:00.000-08:00</published><updated>2008-11-12T15:47:23.263-08:00</updated><title type='text'>Using Grails to explore and develop the domain model</title><content type='html'>Data modeling is typically a fairly important design activity. However, I have a hard time with EAR diagrams and data models as a starting point – particularly for a new system (you don’t really have a choice when dealing with legacy databases). Thinking in objects is much more natural to me. Also, I like the idea of using code to explore the domain model and try a few things out.&lt;br /&gt;&lt;br /&gt;In the past, I have used the forward engineering features of Hibernate for this very purpose. Once the tables have been created in the database, I take advantage of a neat feature in JDeveloper where you can reverse engineer a data model diagram. So the process kind of goes like this Object model-&gt;Java code-&gt;Hibernate DDL-&gt;Database-&gt;Data model. It worked well in thrashing out the data model for my last project. Of course, it may not work for everyone but that’s ok – it works for me and that’s all that matters ;-)&lt;br /&gt;&lt;br /&gt;For a new project I’m working on, I thought I’d try something different. I thought I’d give Grails a spin. This worked even better because Grails can generate such a lot of boiler-plate code for me enabling me to move a lot faster. It can also generate a scaffolding UI so I can interact with and test out the model. Here was the process:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Download and install Grails from grails.org &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Follow the Quick Start guide to familiarize yourself with Grails &lt;a href="http://grails.org/Quick+Start"&gt;http://grails.org/Quick+Start&lt;/a&gt; . During the Quick Start guide, you will learn how to create an application a domain class and a controller &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Configure Grails to use Oracle instead of HSQL (I needed to externalize the database so I could browse it and reverse engineer the data model diagram using JDeveloper) – to do this modify grails-app/conf/DataSource.groovy – change the driver class name and the JDBC url – you may also have to copy the Oracle JDBC driver (ojdbc14.jar) to the lib directory &lt;/li&gt;&lt;br /&gt;&lt;li&gt;For each entity (where name is the name of the entity you want to model)&lt;br /&gt;&lt;ol type="a"&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;$ grails create-domain-class&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Modify the generated class located in grails-app/domain/&lt;i&gt;name&lt;/i&gt;.groovy – add the attributes&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;code&gt;$ grails create-controller&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Modify the generated controller class located in grails-app/controllers/nameController.groovy – change the body of the class to look like &lt;code&gt;def scaffold = &lt;i&gt;name&lt;/i&gt;&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Run the application &lt;code&gt;$ grails run-app&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Point your browser to the application&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Populate the model by interacting with the controllers through the generated scaffolding&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Add some more entities by going to back to step 4&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If you want to auto-populate the model with data on startup, add your code to grails-app/conf/BootStrap.groovy&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;I have to say, I found the out of the box experience with Grails pretty polished. It passed the 15 minute test with flying colors. (If I can’t get something working in under 15 minutes, I tend to dump it).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-8015451593665060560?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/8015451593665060560/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=8015451593665060560' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/8015451593665060560'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/8015451593665060560'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/11/using-grails-to-explore-and-develop.html' title='Using Grails to explore and develop the domain model'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-4484022055300542458</id><published>2008-10-08T14:40:00.000-07:00</published><updated>2008-10-08T14:59:23.470-07:00</updated><title type='text'>Performance testing tip - assumptions are dangerous</title><content type='html'>I've been doing a lot of research over the past couple of weeks with a goal to understand how cryptography performs on different platforms, with different algorithms, with different data characteristics. In addition, I was also interested to learn how crypto accelerators perform compared to software based crypto.&lt;br /&gt;&lt;br /&gt;I can't share all the findings with you, but I can tell you I was quite surprised by what I found. Once again, I was reminded that assumptions regarding performance are invariably wrong. The reason for this is there are so many subtle factors involved that can affect performance. Also, technology is constantly moving forward. Results and conclusions found today may not necessarily hold true tomorrow. As an example, does anybody remember how people used to chastise Java for being slow. That may have been the case in 1996, but in 2008 it most certainly is not.&lt;br /&gt;&lt;br /&gt;A major assumption I had before I conducted my research was that I was convinced that the crypto accelerator would outperform s/w based crypto. This, it turned out was not the case. There were some cases where the s/w based crypto outperformed the crypto accelerator significantly. I also assumed that the SPARC chip would perform pretty well compared to AMD/Intel chips - it didn't really perform well at all in terms of raw speed/performance for the kinds of crypto operations I was performing.&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_SIJ9fKIpUKU/SO0sTXC-V3I/AAAAAAAAAAY/v8Ft4pc0i8c/s1600-h/sha1hash.PNG"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_SIJ9fKIpUKU/SO0sTXC-V3I/AAAAAAAAAAY/v8Ft4pc0i8c/s320/sha1hash.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5254905051326863218" /&gt;&lt;/a&gt;&lt;br /&gt;The graph to the left shows the fastest and average performance for performing a SHA-1 hash on a credit card number on various platforms. The units are in nanoseconds. Look how fast Java performs on Intel on both Linux and Windows.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-4484022055300542458?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/4484022055300542458/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=4484022055300542458' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/4484022055300542458'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/4484022055300542458'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/10/performance-testing-tip-assumptions-are.html' title='Performance testing tip - assumptions are dangerous'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SIJ9fKIpUKU/SO0sTXC-V3I/AAAAAAAAAAY/v8Ft4pc0i8c/s72-c/sha1hash.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-4623457275824230308</id><published>2008-09-13T09:03:00.000-07:00</published><updated>2008-09-13T09:08:27.747-07:00</updated><title type='text'>Quote of the day - Build one to throw it away</title><content type='html'>This is a quote from Fred Brooks' "&lt;a href="http://en.wikipedia.org/wiki/The_Mythical_Man-Month"&gt;The Mythical Man Month&lt;/a&gt;".&lt;br /&gt;&lt;br /&gt;“Where a new system concept or new technology is used, one has to build a system to throw away, for even the best planning is not so omniscient as to get it right the first time. Hence plan to throw one away; you will, anyhow.”&lt;br /&gt;&lt;br /&gt;Great insight. What's also remarkable is this book is over thirty years old. In technology, we like to talk about how much and how fast things change - and they do, however there appear to be lots of things that remain timeless.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-4623457275824230308?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/4623457275824230308/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=4623457275824230308' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/4623457275824230308'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/4623457275824230308'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/09/quote-of-day-build-one-to-throw-it-away.html' title='Quote of the day - Build one to throw it away'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-1421646141479078725</id><published>2008-09-12T12:51:00.001-07:00</published><updated>2008-09-13T08:54:32.367-07:00</updated><title type='text'>Using the Flickr Authentication API - part I - generating the api_sig</title><content type='html'>All Flickr API calls using an authentication token must be signed. In addition, calls to the flickr.auth.* methods and redirections to the auth page on flickr must also be signed.&lt;br /&gt;&lt;br /&gt;The api_sig is to be appended to the URL for all such API calls. The following code can be used to generate the api_sig (of course, you can use a higher-level library such as &lt;a href="http://sourceforge.net/projects/flickrj/"&gt;flickrj&lt;/a&gt; if you enjoy eating microwave ready to eat meals over growing your own tomatoes ;-).&lt;br /&gt;&lt;br /&gt;&lt;pre style="BORDER-RIGHT: #999999 1px dashed; PADDING-RIGHT: 5px; BORDER-TOP: #999999 1px dashed; PADDING-LEFT: 5px; FONT-SIZE: 12px; PADDING-BOTTOM: 5px; OVERFLOW: auto; BORDER-LEFT: #999999 1px dashed; WIDTH: 100%; COLOR: #000000; LINE-HEIGHT: 14px; PADDING-TOP: 5px; BORDER-BOTTOM: #999999 1px dashed; FONT-FAMILY: Andale Mono, Lucida Console, Monaco, fixed, monospace; BACKGROUND-COLOR: #eee"&gt;&lt;code&gt;package com.jasondchambers.flickr;&lt;br /&gt;import java.security.MessageDigest;&lt;br /&gt;import java.security.NoSuchAlgorithmException;&lt;br /&gt;import java.util.Set;&lt;br /&gt;import java.util.SortedMap;&lt;br /&gt;import java.util.TreeMap;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* Generates an api_sig from a list of parameter key value pairs&lt;br /&gt;*&lt;br /&gt;* The signing process goes like this. The parameters are sorted&lt;br /&gt;* e.g. foo=1, bar=2, baz=3 sorts to bar=2, baz=3, foo=1&lt;br /&gt;* The secret and the parameters are concatenated together as follows to provide&lt;br /&gt;* the raw signature&lt;br /&gt;* e.g. SECRETbar2baz3foo1&lt;br /&gt;* An MD5 hash is created, converted to hex and returned - this is to be used as&lt;br /&gt;* the api_sig parameter&lt;br /&gt;*&lt;br /&gt;* See section 8 of [1]&lt;br /&gt;*&lt;br /&gt;* [1] http://www.flickr.com/services/api/auth.spec.html&lt;br /&gt;*&lt;br /&gt;* @author Jason Chambers&lt;br /&gt;*&lt;br /&gt;*/&lt;br /&gt;public class ApiSigGenerator {&lt;br /&gt;&lt;br /&gt; private String secret;&lt;br /&gt;&lt;br /&gt; public ApiSigGenerator(String secret) {&lt;br /&gt;   this.secret = secret;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String sign(String... paramKeyValuePairs) {&lt;br /&gt;   try {&lt;br /&gt;     // Sort the parameters first&lt;br /&gt;     SortedMap&amp;lt;String, String&amp;gt; sortedParameterMap = sort(paramKeyValuePairs);&lt;br /&gt;     // Generate the raw signature&lt;br /&gt;     String rawApiSig = generateRawApiSig(sortedParameterMap);&lt;br /&gt;     // Hash the raw signature and return&lt;br /&gt;     return generateMd5(rawApiSig.toString());&lt;br /&gt;   }&lt;br /&gt;   catch (Exception e) {&lt;br /&gt;     throw new FlickrClientException(e);&lt;br /&gt;   }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private SortedMap&amp;lt;String, String&amp;gt; sort(String[] paramKeyValuePairs) {&lt;br /&gt;   SortedMap&amp;lt;String, String&amp;gt; sortedParameterMap = new TreeMap&amp;lt;String, String&amp;gt;();&lt;br /&gt;   final int KEY = 0;&lt;br /&gt;   final int VALUE = 1;&lt;br /&gt;   int i = KEY;&lt;br /&gt;   String key = null;&lt;br /&gt;   for (String o : paramKeyValuePairs) {&lt;br /&gt;     if (i == KEY) {&lt;br /&gt;       key = o;&lt;br /&gt;     } else {&lt;br /&gt;       sortedParameterMap.put(key, o);&lt;br /&gt;     }&lt;br /&gt;     i = ~i;&lt;br /&gt;   }&lt;br /&gt;   return sortedParameterMap;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private String generateRawApiSig(SortedMap&amp;lt;String, String&amp;gt; sortedParameterMap) {&lt;br /&gt;   StringBuffer rawApiSig = new StringBuffer();&lt;br /&gt;   rawApiSig.append(secret);&lt;br /&gt;   Set&amp;lt;String&amp;gt; keySet = sortedParameterMap.keySet();&lt;br /&gt;   for (String k1 : keySet) {&lt;br /&gt;     rawApiSig.append(k1);&lt;br /&gt;     rawApiSig.append(sortedParameterMap.get(k1));&lt;br /&gt;   }&lt;br /&gt;   return rawApiSig.toString();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private static String generateMd5(String input) throws NoSuchAlgorithmException {&lt;br /&gt;   StringBuffer output = new StringBuffer();&lt;br /&gt;   MessageDigest md;&lt;br /&gt;     md = MessageDigest.getInstance("MD5");&lt;br /&gt;   byte[] md5 = md.digest(input.getBytes());&lt;br /&gt;   for (int i = 0; i &amp;lt; md5.length; i++) {&lt;br /&gt;     String tmpStr = "0" + Integer.toHexString((0xff &amp;amp; md5[i]));&lt;br /&gt;     output.append(tmpStr.substring(tmpStr.length() - 2));&lt;br /&gt;   }&lt;br /&gt;   return output.toString();&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-1421646141479078725?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/1421646141479078725/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=1421646141479078725' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/1421646141479078725'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/1421646141479078725'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/09/using-flickr-authentication-api-part-i.html' title='Using the Flickr Authentication API - part I - generating the api_sig'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-8271197986666050072</id><published>2008-09-10T14:51:00.000-07:00</published><updated>2008-09-10T14:55:08.153-07:00</updated><title type='text'>Google's innovative approach to communicating Chrome</title><content type='html'>A big part of my job is communicating. In particular, I communicate designs and vision such that our development team can go off and build product. I have tried several approaches to communicating designs to the (remote) development team over the past few years or so including (some with more success than others):&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Powerpoints (2007 has awesome 3D capabilities – ideal for those m-architectures)&lt;/li&gt;&lt;li&gt;Demonstrations (I use &lt;a href="http://www.shinywhitebox.com/home/home.html"&gt;iShowU&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Proof-of-concepts (i.e. working prototype code)&lt;/li&gt;&lt;li&gt;Visio’s (for when I feel constrained by UML)&lt;/li&gt;&lt;li&gt;Word documents (nice and formal)&lt;/li&gt;&lt;li&gt;Recorded Video presentations (I have found demonstration software works better)&lt;/li&gt;&lt;li&gt;UML models (using Enterprise Architect)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Why do I bring this up? -  Because I have been studying Google Chrome of late. The thing that most impressed me most was not necessarily Google Chrome per se (although I applaud some of their innovations and their approach), but one of the innovative ways they communicated the project to the public when it was launched.&lt;br /&gt;&lt;br /&gt;They developed a comic-book – go check it &lt;a href="http://www.google.com/googlebooks/chrome/index.html"&gt;out&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-8271197986666050072?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/8271197986666050072/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=8271197986666050072' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/8271197986666050072'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/8271197986666050072'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/09/googles-innovative-approach-to.html' title='Google&apos;s innovative approach to communicating Chrome'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-6717981146359016809</id><published>2008-09-09T16:29:00.000-07:00</published><updated>2008-09-09T16:37:05.634-07:00</updated><title type='text'>Flickr API</title><content type='html'>I have a ton of old prints I'd like to get published on-line at Flickr. I have a scanner at home but that sounds like a tedious process to me. It turns out that Walgreens (a US pharmacy) provides scanning services at a reasonable price. Sounds like a winner.&lt;br /&gt;&lt;br /&gt;Typically whenever I publish photos to Flickr, I use iPhoto with FlickrExport from Connected Flow. This works fine for a manageable number of photos.&lt;br /&gt;&lt;br /&gt;I'm thinking I want a better approach for bulk uploads so I signed up for an API key and intend to write some code to manage and automate the upload process for me.&lt;br /&gt;&lt;br /&gt;I'll keep you posted with my findings and experiences using the API in due course.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-6717981146359016809?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/6717981146359016809/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=6717981146359016809' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/6717981146359016809'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/6717981146359016809'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/09/flickr-api.html' title='Flickr API'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-4056379073239204909</id><published>2008-09-05T14:31:00.000-07:00</published><updated>2008-09-06T10:13:37.325-07:00</updated><title type='text'>iPhone - don't leave home without it</title><content type='html'>It's been a couple of weeks since I picked up the new iPhone 3G. It had been on my shopping list for quite a while but being a bit of a cheapskate, I decided to wait and defer the purchase for as long as possible. The good thing about holding out on your technology purchases is that the gadgets only continue to get better (of course this is traded-off against the cost of going without).&lt;br /&gt;&lt;br /&gt;Unfortunately, or fortunately whichever way you look at it, my RAZR finally packed up while on vacation in Kiawah, S.C. I happened to be passing the Apple store in nearby Charleston and decided to pop in and just see if by chance they had any iPhones in stock. To my delight and surprise they had and so I picked one up.&lt;br /&gt;&lt;br /&gt;I immediately put it to use to help me find the car park - using it as a GPS locator if you will (&lt;del&gt;although I don't think it doesn't actually uses GPS - I believe it locates via triangulation with cell towers&lt;/del&gt; correction - it does actually  use GPS and cell tower location technology).&lt;br /&gt;&lt;br /&gt;Immediately, I found the user interface very intuitive. This is a major improvement over the cell phones I have used in the past. The RAZR phone had a terrible user interface. After using it for over two years, I could not identify the correct buttons to press to switch between silence/vibrate and loud ringtone. I ended up just pushing the buttons on the side until the display told me I'd finally reached my desired setting.&lt;br /&gt;&lt;br /&gt;I have to hand it to Apple. They have created a very powerful capable device in the iPhone and have yet managed to make it's operation easier to use than less capable devices.&lt;br /&gt;&lt;br /&gt;This past weekend, I ended up taking my Dad to an urgent care facility in Nashville to get a prescription for Diabetes meds he left back in Atlanta. After waiting 4 hours to see the doctor (we could have driven back to Atlanta in only 3.5 hours), we learned that the doctor had not heard of the medication. I couldn't quite believe what I was hearing. Out comes the iPhone and off I trot over to Google. It turns out the drug in question is not FDA approved (although it is approved in the UK). The doctor confirmed my findings after talking to a pharmacist.&lt;br /&gt;&lt;br /&gt;Also, this past weekend we bought a new car. I did some preliminary research by tapping into edmunds.com when the salesman wasn't looking proved useful in figuring out if I had a good deal or not.&lt;br /&gt;&lt;br /&gt;Finally, I have a terrible short term memory. I'm not very good at remembering things like where I parked the car or shopping lists. I use SpeakEasy which I use to record voice notes and reminders. Invaluable little tool.&lt;br /&gt;&lt;br /&gt;I'm also experimenting with Evernote too as a way to organize some of the chaos - or at least make it accessible when I need it. I used it to compile and collect research for the recent car purchase. Good stuff.&lt;br /&gt;&lt;br /&gt;The one down-side is that the battery, particularly when using 3G doesn't seem to last too long. Also, not quite sure why iTunes Store only works when connected over WiFi.&lt;br /&gt;&lt;br /&gt;The touch-screen device seems to have just the right level of sensitivity.&lt;br /&gt;&lt;br /&gt;All in all though, it's a very useful device.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-4056379073239204909?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/4056379073239204909/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=4056379073239204909' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/4056379073239204909'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/4056379073239204909'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/09/iphone-dont-leave-home-without-it.html' title='iPhone - don&apos;t leave home without it'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-3325330925711368989</id><published>2008-09-02T12:59:00.000-07:00</published><updated>2008-09-02T13:05:57.834-07:00</updated><title type='text'>Lucid definition of design</title><content type='html'>I have just watched an interesting video of a presentation Richard P. Gabriel gave at QCon in 2007. The title of the presentation "Architectures of extraordinarily large. self-sustaining Systems" is available &lt;a href="http://www.infoq.com/presentations/arch-extraordinarily-large-systems"&gt;here&lt;/a&gt; for those of you who are interested.&lt;br /&gt;&lt;br /&gt;During the video, Richard provided a wonderful definition of the term design:&lt;br /&gt;&lt;br /&gt;"&lt;em&gt;Design is the thinking one does before building&lt;/em&gt;"&lt;br /&gt;&lt;br /&gt;I like this definition. What do you think?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-3325330925711368989?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/3325330925711368989/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=3325330925711368989' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/3325330925711368989'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/3325330925711368989'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/09/lucid-definition-of-design.html' title='Lucid definition of design'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-1446350607583184217</id><published>2008-08-27T14:54:00.000-07:00</published><updated>2008-08-27T15:02:54.165-07:00</updated><title type='text'>Why GWT matters - part II</title><content type='html'>Back in May, I wrote about why I &lt;a href="http://www.jasondchambers.com/2008/05/why-gwt-matters-part-i.html"&gt;thought GWT matters&lt;/a&gt;. As promised, I am finally getting around to completing my thoughts on the subject.&lt;br /&gt;&lt;br /&gt;In the original post, I talked about how I felt we took a step back when we shifted our development focus more to the web platform and further away from the desktop. We lost some of the richness of the interface for our users and we retreated from advances in programming models in the areas of component and event based programming. It’s only been fairly recently where we finally can have it all (reach/zero deployment with productive programming models).&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/webtoolkit/"&gt;GWT&lt;/a&gt; does provide it all as far as I’m concerned. It has a nice programming model that should feel natural to anyone that has done any desktop based GUI programming in the past. It doesn’t lock you into a particular widget library which makes it a sound investment. It takes care of dealing with the nastiness of writing cross-browser code – because it generates all of this for you.&lt;br /&gt;&lt;br /&gt;It makes it easy to write internationalized applications. It makes it easy to provide a working back-button for your users (a rarity for a lot of AJAX applications – hit the back button and you may be taken completely out of the application).&lt;br /&gt;&lt;br /&gt;If you really do have to drop-down to JavaScript, there is a mechanism called JSNI which provides this.&lt;br /&gt;&lt;br /&gt;However, most of all it is the amount of attention the GWT team has spent on tool support that really gets my vote. The integration with Eclipse makes for a very productive environment. &lt;br /&gt;&lt;br /&gt;Of course, there are lots of alternatives in the RIA space. Flash/Flex, Silverlight, and Volta are just a couple of alternatives. Of course, if you are lucky enough to have exceptional JavaScript developers on your team then you may feel comfortable using JavaScript directly (although, I have to say although I like dynamically typed languages, one cannot argue with the ability of statically typed languages to assist you in finding problems earlier in the development cycle - i.e. compile-time vs run-time).&lt;br /&gt;&lt;br /&gt;However, GWT seemed to be an ideal fit for my team (no new tools – continue to use Eclipse, no new languages to learn – continue to use Java, and no need to worry about the plethora of browser types and versions).&lt;br /&gt;&lt;br /&gt;What are your experiences with GWT or RIA approaches in general? (the good, the bad, the ugly)?&lt;br /&gt;&lt;a href="http://www.jasondchambers.com/2008/05/why-gwt-matters-part-i.html"&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-1446350607583184217?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/1446350607583184217/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=1446350607583184217' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/1446350607583184217'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/1446350607583184217'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/08/why-gwt-matters-part-ii.html' title='Why GWT matters - part II'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-3837892423454546726</id><published>2008-08-20T10:48:00.001-07:00</published><updated>2008-08-20T10:52:10.850-07:00</updated><title type='text'>Balancing “YAGNI” with “avoiding the cul-de-sac”</title><content type='html'>This is a tough part of the architect’s job because it involves speculation. It would be easier if we had a crystal ball to peer into, but we don’t. All we have to go on is our instinct based on past experiences.&lt;br /&gt;&lt;br /&gt;Let’s look at one end of the spectrum – “Avoiding the cul-de-sac”. You may have been burnt in the past due to significant amount of rework required as requirements changed or new requirements surfaced during the middle of a project. The common knee-jerk reaction is to make everything flexible, configurable and adaptable. However, like most engineering endeavors there are always trade-offs to be made. Be careful, you may have ended up with un-necessary complexity (which drives up the cost of the project).&lt;br /&gt;&lt;br /&gt;Let’s look at the other end of the spectrum – You Ain’t Gonna Need It. With this approach, you eschew flexibility, configurability and adaptability until a requirement surfaces that demands it. This approach is a gamble, but the agile folks will tell you that this is ok because of continuous refactoring and unit tests.&lt;br /&gt;&lt;br /&gt;So what is the architect to do? Well, it depends. You have to look at the sub-systems of the architecture and run through some what-if scenarios. To prioritize, I would focus on the sub-systems that have external dependencies. Wherever you have external dependencies, you have given up control. Anything could happen to the external dependency and you have no control over it. The vendor or organization that owns it could a) go out of business b) change their licensing model c) raise their prices d) break backwards compatibility e) discontinue support f) fail to meet performance SLAs. The architect is responsible for addressing these risks in such a way that if any of these events would happen, the impact is contained.&lt;br /&gt;&lt;br /&gt;The single most effective way of containing impact is to effectively manage the degrees of coupling within the system. A Formula 1 team can change the wheel in under 10s because the wheel component is loosely (yet securely) coupled to the car. How long would it take to switch your system from say Oracle to SQL Server?&lt;br /&gt;&lt;br /&gt;Where couplings between components are legitimate, there are several coding techniques and (albeit very similar) design patterns you can use to allow for independent evolution of the components yielding contained impact in the event of change. On the coding side, coupling to an interface rather than implementation is a safe bet – thus allowing for different implementations to be plugged in later. On the design front, the strategy, provider and adaptor patterns all help to introduce flexibility into the architecture. For example, the Java Cryptography Extensions (JCE) is a great example of the provider pattern. Initially, I may use a software based provider that performs cryptographic services – but I may upgrade to a hardware based provider (HSM) for when the time comes when a performance boost is required – all without having to change any code. A more common example is the JDBC architecture, where drivers can be plugged in to connect the application to a different database product for its persistence needs.&lt;br /&gt;&lt;br /&gt;A goal of the architecture is therefore to provide flexibility without disruption where it makes sense.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-3837892423454546726?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/3837892423454546726/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=3837892423454546726' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/3837892423454546726'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/3837892423454546726'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/08/balancing-yagni-with-avoiding-cul-de.html' title='Balancing “YAGNI” with “avoiding the cul-de-sac”'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-6848217311215726839</id><published>2008-08-14T17:41:00.000-07:00</published><updated>2008-08-14T17:42:44.995-07:00</updated><title type='text'>Do Architects need coding skills?</title><content type='html'>Do Architects need coding skills?&lt;br /&gt;&lt;br /&gt;In the oft cited parallel world of construction and engineering (of buildings), it is true that to be an architect one does not need to necessarily possess brick-laying skills in order to be effective in their role. However, to be an architect one does need to understand the properties of the materials used for construction.&lt;br /&gt;&lt;br /&gt;What about software? Most, in fact pretty much all of the (software) architects I know have some background coding experience. The question to be asked is how essential is it to have coding skills? If an architect can get by without having ever laid a brick, is it possible for a software architect to get by without ever written a line of code?&lt;br /&gt;&lt;br /&gt;At least at the current state of our discipline, I would say no. &lt;br /&gt;&lt;br /&gt;However, a more contentious question maybe – Do Architects need to maintain their coding skills once they’ve achieved the lofty status of architect. Well, I think it depends. Enterprise architects - possibly not (more on that later). However, for application, solution or product architects – I would say yes. There are many reasons why I think this is the case.&lt;br /&gt;&lt;br /&gt;Firstly, the job of the architect is to take a definition of what needs to be built and figure out the best way for how to deliver the solution or product given the imposed time and financial constraints (in reality, the architect is also usually involved in figuring out what needs to be built although this should really be the role of the business analyst or product manager). He/she needs to figure out how to break the problem into manageable chunks (high-level design), decide what materials and tools to use (taking into account the current state of the art and the skills of the development organization) and forage for the non-functional requirements or quality attributes. Basically, the objective of the architect is to deliver better faster cheaper. It’s often a difficult job. The architect may also be stretched and be responsible for data design, dynamic modeling and even detailed design activities such as screen design. Essentially, the architect has to provide whatever it takes to ensure the development organization has enough information to be successful in the build out.&lt;br /&gt;&lt;br /&gt;The architect may also be pulled into oversight of the development effort. Throwing designs over the wall and turning your back is likely to result in the development organization delivering something different to what is actually required. &lt;br /&gt;&lt;br /&gt;Sometimes, models are not enough. They are by their nature abstract which means they are missing details. The details are where the devil lies. The details need to be figured out sooner or later and so the development organization may either choose to figure it out for themselves or involve the architect. &lt;br /&gt;&lt;br /&gt;There may be some system requirements that are difficult to articulate in English specifications. Sometimes, code in the form of prototypes provides an ideal communication mechanism to the development team. &lt;br /&gt;The architect is required to look around and observe and evaluate current trends in technology and processes. Maybe, there is some new toolkit or framework that has been developed that promises to help shorten the development cycle or provide some significant value. Of course, the architect has to try out the technology or process outside of the critical path of the project to ensure fit and readiness for the project at hand. Perhaps some prototypes need to be developed. The architect may need to dive in and learn the new technology before making a sound recommendation to embrace or avoid.&lt;br /&gt;&lt;br /&gt;The architect shouldn’t be coding on a daily basis. If the architect is spending most of his/her time then I would argue they are not doing architecture. Having said that, coding is a small yet important component of the architect’s repertoire.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-6848217311215726839?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/6848217311215726839/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=6848217311215726839' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/6848217311215726839'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/6848217311215726839'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/08/do-architects-need-coding-skills.html' title='Do Architects need coding skills?'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-1581956280861734011</id><published>2008-07-25T08:35:00.000-07:00</published><updated>2008-07-25T08:40:05.143-07:00</updated><title type='text'>Caching tips</title><content type='html'>Caching is all about keeping data closest to where it is needed. It is first and foremost a performance optimization, however you have to ask yourself a couple of questions about the data:&lt;br /&gt;&lt;br /&gt;1. Is it read-only, read-mostly or write-mostly?&lt;br /&gt;2. How often does the data change? &lt;br /&gt;3. What is the tolerance in each case for data staleness?&lt;br /&gt;&lt;br /&gt;Data that falls into the category of read-only/mostly, has a low rate of change and a reasonable tolerance for data staleness are ideal candidates for caching. A key performance metric is cache-hits versus cache-misses. Obviously, you want to look for cache-hits to get a good return on the investment. If you get few cache-hits and lots of cache-misses - be careful, you may need to rethink your caching strategy as you may have made performance worse rather than better.&lt;br /&gt;&lt;br /&gt;For caching web-objects for a global web-site, edge caching providers such as Akamai are the way to go. This works well for static objects such as images, CSS, JS, HTML etc. It moves the content closer to the end user. Also, recall that every point across the network may have some degree of cache-ability (e.g. proxy servers, routers, browsers).&lt;br /&gt;&lt;br /&gt;Ensure the HTTP cache-control headers are set appropriately to reduce trips back to the server when the version in the browser cache could be plenty fresh (enough)&lt;br /&gt;&lt;br /&gt;For caching data on the app server that is sourced from some back-end service, database or EIS system, memcached or JBossCache work well. Clustering/cache replication may not always be required. Clustering is a good option if the data source is the end-user - if the data source is a database, you can recover the cache quite easily - if the data source is the end user (e.g. session information), you cannot very well ask them, "My cache seems to have gone down the toilet taking your session information with it - tell me again, how many shares of IBM did you want to buy again? (sorry)"&lt;br /&gt;&lt;br /&gt;You can reduce a significant amount of load on back-end resources through caching - but only if the data is a good candidate for caching. Hibernate/eh-cache may work pretty well here in addition to memcached and JBossCache.&lt;br /&gt;&lt;br /&gt;Oracle Coherence in my opinion has evolved way beyond it's caching roots. Using Coherence, you can build a huge data grid across commodity hardware. Each node in the grid has it's share of responsibilities and is backed up by n other nodes for redundancy. Operations against the data can move to where the data is rather than move the data to where the operation occurs (our traditional way of approaching data processing for the past gozillion years).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-1581956280861734011?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/1581956280861734011/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=1581956280861734011' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/1581956280861734011'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/1581956280861734011'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/07/caching-tips.html' title='Caching tips'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-6203688548367626164</id><published>2008-07-23T17:53:00.000-07:00</published><updated>2008-12-04T07:54:44.123-08:00</updated><title type='text'>Tomcat Controller - updated</title><content type='html'>&lt;strong&gt;Update 12/4/08&lt;/strong&gt;&lt;br /&gt;Thanks to everyone's comments, we took a deeper look and found what we were looking for in JMX. We plan on leveraging a single JVM/Tomcat instance with seperate contexts. The Tomcat Admin web application has the MBeans we were looking for enabling us to dynamically add/configure data sources.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Original post&lt;/strong&gt;&lt;br /&gt;I have a requirement that calls for breaking the product I am designing into multiple JVMs (Tomcat instances to be precise). The original plan was to have a single Tomcat instance with multiple contexts however that plan had to be shelved for various reasons. One of the Tomcat instances is to support the Management GUI. To cut a long story short, the Management GUI is required to manage the life-cycle (i.e. stop/start) of another Tomcat instance containing the core engine of the product.&lt;br /&gt;&lt;br /&gt;I explored several options for approaching this problem. My first attempt was to look at OSGi. I have been eager to explore OSGi for quite a while now so this gave me a perfect opportunity. I managed to knock out a couple of simple bundles and deploy them to Equinox. Next step, try and build a bundle out of Tomcat. Unfortunately, this didn’t quite go to plan. Tomcat seems to have an issue with authority – it wouldn’t play nicely in an OSGi world leaving me with a stack trace of exception I didn’t have the time or energy to track down. I did think about switching to Jetty at this point, but this would be a very disruptive change and one that our schedule couldn’t allow.&lt;br /&gt;&lt;br /&gt;Moving on, I recalled that Eclipse WTP does a pretty good job in managing server definitions and controlling their lifecycles. I proceeded to download the source for WTP and found a bunch of code called Server Tools. I tried to reverse engineer the structure from the code but was quite astonished by the size of the code base. I figured it would be too much to weed through to get to what I was looking for. The Server Tools is pretty well structured code – it’s just overkill for my needs.&lt;br /&gt;&lt;br /&gt;Next step, there has to be something in Ant I can use. I remember using the Ant Catalina tools from years ago. These are basically Ant tasks that expose the controls of Tomcat through the Manager application to Ant. The tasks didn’t really give me what I was looking for. Sure, they could be used to control individual contexts but didn’t really give me anything to control the server instance itself.&lt;br /&gt;&lt;br /&gt;I was really looking to avoid Runtime.exec() if I could. There had to be something that worked at a higher level of abstraction. I continued to dig around Ant and recalled the Java &lt;java&gt;task. This sounded promising. Of course, I really wanted to leverage the class behind the Java task itself and not deal with build.xml, launching Ant and all that malarchy. The Java task provides me with what I wanted. Here’s the prototype code I came up with called the Tomcat Controller. I think the Java task fits my needs perfectly:&lt;br /&gt;&lt;br /&gt;TomcatController.java&lt;br /&gt;&lt;br /&gt;&lt;pre style="BORDER-RIGHT: #999999 1px dashed; PADDING-RIGHT: 5px; BORDER-TOP: #999999 1px dashed; PADDING-LEFT: 5px; FONT-SIZE: 12px; PADDING-BOTTOM: 5px; OVERFLOW: auto; BORDER-LEFT: #999999 1px dashed; WIDTH: 100%; COLOR: #000000; LINE-HEIGHT: 14px; PADDING-TOP: 5px; BORDER-BOTTOM: #999999 1px dashed; FONT-FAMILY: Andale Mono, Lucida Console, Monaco, fixed, monospace; BACKGROUND-COLOR: #eee"&gt;&lt;code&gt;&lt;br /&gt;import java.io.File;&lt;br /&gt;&lt;br /&gt;import org.apache.tools.ant.Project;&lt;br /&gt;import org.apache.tools.ant.taskdefs.Java;&lt;br /&gt;import org.apache.tools.ant.types.Path;&lt;br /&gt;&lt;br /&gt;public class TomcatController {&lt;br /&gt;&lt;br /&gt;  public enum State {&lt;br /&gt;    STARTING,&lt;br /&gt;    STARTED,&lt;br /&gt;    STOPPING,&lt;br /&gt;    STOPPED&lt;br /&gt;  };&lt;br /&gt; &lt;br /&gt;  State state = State.STOPPED;&lt;br /&gt; &lt;br /&gt;  State getState() {&lt;br /&gt;    return state;&lt;br /&gt;  }&lt;br /&gt; &lt;br /&gt;  private TomcatServerConfiguration config;&lt;br /&gt; &lt;br /&gt;  public TomcatController(TomcatServerConfiguration config) {&lt;br /&gt;    this.config = config;&lt;br /&gt;  }&lt;br /&gt; &lt;br /&gt;  // Asynchronously start this Tomcat&lt;br /&gt;  public synchronized void start() {&lt;br /&gt;    state = State.STARTING;&lt;br /&gt;    Java javaTask = createTomcatJavaTask();&lt;br /&gt;    javaTask.createArg().setValue("start");&lt;br /&gt;    for (String s : config.getJvmArgs())&lt;br /&gt;      javaTask.createJvmarg().setValue(s);&lt;br /&gt;    javaTask.setFork(true);&lt;br /&gt;    javaTask.setSpawn(true);&lt;br /&gt;&lt;br /&gt;    System.out.println(javaTask.getCommandLine());&lt;br /&gt;    javaTask.execute();   &lt;br /&gt;    state = State.STARTED;// Not quite true - Tomcat may still be starting at this point&lt;br /&gt;  }&lt;br /&gt; &lt;br /&gt;  // Block while this Tomcat is stopping &lt;br /&gt;  public synchronized void stop() {&lt;br /&gt;    state = State.STOPPING;&lt;br /&gt;    Java javaTask = createTomcatJavaTask();&lt;br /&gt;    javaTask.createArg().setValue("stop");&lt;br /&gt;    javaTask.setFork(true);&lt;br /&gt;    javaTask.setSpawn(false);&lt;br /&gt;&lt;br /&gt;    System.out.println(javaTask.getCommandLine());&lt;br /&gt;    javaTask.execute();&lt;br /&gt;    state = State.STOPPED;   &lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private Java createTomcatJavaTask() {&lt;br /&gt;    Project project = new Project();&lt;br /&gt;    project.setBaseDir(new File(config.getTomcatHome()+File.separator+"bin"));&lt;br /&gt;    Java javaTask = new Java();&lt;br /&gt;    javaTask.setProject(project);&lt;br /&gt;    javaTask.setDir(new java.io.File(config.getTomcatHome()+File.separator+"bin"));&lt;br /&gt;    javaTask.setTaskName("tomcat");&lt;br /&gt;    javaTask.setClassname("org.apache.catalina.startup.Bootstrap");&lt;br /&gt;   &lt;br /&gt;    Path classpath = new Path(project);&lt;br /&gt;    classpath.createPathElement().setPath(config.getTomcatHome()+File.separator+"bin");&lt;br /&gt;    classpath.createPathElement().setPath(config.getTomcatHome()+File.separator+"bin"+File.separator+"bootstrap.jar");&lt;br /&gt;    classpath.createPathElement().setPath(config.getTomcatHome()+File.separator+"bin"+File.separator+"commons-logging-api.jar");&lt;br /&gt;    javaTask.setClasspath(classpath);&lt;br /&gt;    javaTask.createJvmarg().setValue("-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager");&lt;br /&gt;    javaTask.createJvmarg().setValue("-Djava.util.logging.config.file="+config.getTomcatHome()+File.separator+"conf"+File.separator+"logging.properties");&lt;br /&gt;    javaTask.createJvmarg().setValue("-Djava.endorsed.dirs="+config.getTomcatHome()+File.separator+"common"+File.separator+"endorsed");&lt;br /&gt;    javaTask.createJvmarg().setValue("-Dcatalina.base="+config.getTomcatHome());&lt;br /&gt;    javaTask.createJvmarg().setValue("-Dcatalina.home="+config.getTomcatHome());&lt;br /&gt;    javaTask.createJvmarg().setValue("-Djava.io.tmpdir="+config.getTomcatHome()+File.separator+"temp");&lt;br /&gt;    return javaTask;&lt;br /&gt;  }&lt;br /&gt; &lt;br /&gt;  public static void main(String[] args) throws InterruptedException {&lt;br /&gt;   &lt;br /&gt;    // Quick test - should really be in a JUnit test&lt;br /&gt;    TomcatServerConfiguration config = new TomcatServerConfiguration();&lt;br /&gt;    config.setName("Engine");&lt;br /&gt;    config.setTomcatHome("c:\\apache-tomcat-5.5.26");&lt;br /&gt;    config.addJvmArg("-Xmx512m");&lt;br /&gt;    config.addJvmArg("-Dcom.sun.management.jmxremote");&lt;br /&gt;    config.addJvmArg("-Dcom.sun.management.jmxremote.port=9128");&lt;br /&gt;    config.addJvmArg("-Dcom.sun.management.jmxremote.ssl=false");&lt;br /&gt;    config.addJvmArg("-Dcom.sun.management.jmxremote.authenticate=false");            &lt;br /&gt;    TomcatController tc = new TomcatController(config);&lt;br /&gt;    tc.start();&lt;br /&gt;    Thread.currentThread().sleep(20000);&lt;br /&gt;    tc.stop();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;TomcatServerConfiguration&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="BORDER-RIGHT: rgb(153,153,153) 1px dashed; PADDING-RIGHT: 5px; BORDER-TOP: rgb(153,153,153) 1px dashed; PADDING-LEFT: 5px; FONT-SIZE: 12px; PADDING-BOTTOM: 5px; OVERFLOW: auto; BORDER-LEFT: rgb(153,153,153) 1px dashed; WIDTH: 100%; COLOR: rgb(0,0,0); LINE-HEIGHT: 14px; PADDING-TOP: 5px; BORDER-BOTTOM: rgb(153,153,153) 1px dashed; FONT-FAMILY: Andale Mono,Lucida Console,Monaco,fixed,monospace; BACKGROUND-COLOR: rgb(238,238,238)"&gt;&lt;code&gt;&lt;br /&gt;import java.util.ArrayList;&lt;br /&gt;import java.util.List;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;public class TomcatServerConfiguration {&lt;br /&gt;&lt;br /&gt; private String name;&lt;br /&gt; private String tomcatHome;&lt;br /&gt; private List&amp;lt;String&amp;gt; jvmArgs = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;&lt;br /&gt; public TomcatServerConfiguration() {&lt;br /&gt;&lt;br /&gt; }&lt;br /&gt; public String getName() {&lt;br /&gt;   return name;&lt;br /&gt; }&lt;br /&gt; public TomcatServerConfiguration setName(String name) {&lt;br /&gt;   this.name = name;&lt;br /&gt;   return this;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getTomcatHome() {&lt;br /&gt;   return tomcatHome;&lt;br /&gt; }&lt;br /&gt; public TomcatServerConfiguration setTomcatHome(String tomcatHome) {&lt;br /&gt;   this.tomcatHome = tomcatHome;&lt;br /&gt;   return this;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public List&amp;lt;String&amp;gt; getJvmArgs() {&lt;br /&gt;   return jvmArgs;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void addJvmArg(String arg) {&lt;br /&gt;   jvmArgs.add(arg);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void clearJvmArgs() {&lt;br /&gt;   jvmArgs.clear();&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-6203688548367626164?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/6203688548367626164/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=6203688548367626164' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/6203688548367626164'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/6203688548367626164'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/07/tomcat-controller.html' title='Tomcat Controller - updated'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-6052800806848213786</id><published>2008-06-18T06:34:00.000-07:00</published><updated>2008-06-18T06:40:10.996-07:00</updated><title type='text'>Agile and mountain climbing</title><content type='html'>I love agile. I cannot imagine getting software done in a traditional waterfront/BUFD way ever again.&lt;br /&gt;&lt;br /&gt;Each sprint or iteration is kind of like making camp at the end of the day as you climb a mountain. However, the mountain does not go on forever (although it probably feels that way). You have to reach the summit one day and declare victory (then move on to another mountain - once you descend, recover and replenish supplies of course).&lt;br /&gt;&lt;br /&gt;With agile you end up with a ton of releases accumulating - 1 for the end of each sprint. During the sprints, we pile features and fixes and we pray the documentation and QA can keep up with the pace. These releases are not really intended for external consumption. However, sometimes they may find their way into customers hands. This could happen from time to time.&lt;br /&gt;&lt;br /&gt;However, let's consider the scenario where you had reached 8 sprints after a period of ~8 months (assuming 4 week sprints). You now have accumulated 8 releases in your wake. If each of those 8 releases have found their way into different customers hands, you are obliged to support that release. This means you have to consider 8 different upgrade scenarios. You also have to consider 8 x n different compatibility tests where n is the number of components in the product. This becomes un-manageable.&lt;br /&gt;&lt;br /&gt;What is the solution? The answer is you have to bake into your release cycle a GA release. How many sprints maketh a GA release? It depends. How often should you do a GA release? It depends. The GA release is where you finally say - yes, we've reached the summit. It's time to put this effort to rest. Time to move on to the next mountain.&lt;br /&gt;&lt;br /&gt;Are there any special considerations for the GA release? Yes. The emphasis for the GA release should be all about quality. Often, during the sprint the QA effort is most likely going to be focussed on functional testing. For a GA release, there is so much more testing that needs to be performed.&lt;br /&gt;&lt;br /&gt;The GA release is about slowing down the pace of change and allowing the product to bake.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-6052800806848213786?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/6052800806848213786/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=6052800806848213786' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/6052800806848213786'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/6052800806848213786'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/06/agile-and-mountain-climbing.html' title='Agile and mountain climbing'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-1433822055394674792</id><published>2008-06-16T19:42:00.000-07:00</published><updated>2008-06-16T19:43:16.946-07:00</updated><title type='text'>The parallels between Oil and Relational Database Management Systems</title><content type='html'>We are addicted to Relational Database Management Systems. It's a safe, well-understood mature technology. Whenever we have&lt;br /&gt;persistence requirements, our knee-jerk reaction is typically "let's stick it in a relational database". And why not indeed. It's not &lt;br /&gt;a bad choice, and in some cases it may be your only choice.&lt;br /&gt;&lt;br /&gt;Along the way, there have been some contenders to the crown. OODBMS - Object Oriented Database Management Systems spring to mind. But they never really took off, finding themselves pushed into a niche.&lt;br /&gt;&lt;br /&gt;It kind of reminds me of our dependence on oil for energy. Instead of OPEC though, we have IBM, Oracle and Microsoft plus&lt;br /&gt;I would include Sun (MySQL). I'd probably get slammed if I didn't include PostgreSQL in the mix too (although I would say this&lt;br /&gt;is probably not high octane).&lt;br /&gt;&lt;br /&gt;However, in 2008 things are a little different. There are some worthy alternatives you may want to check out. So what are the alternatives? Well, we have data grids. There are a bunch to chose from. Oracle Coherence is one that I first became familiar&lt;br /&gt;with through my work at Delta (back when it was Tangosol). There are others available too now for both Java and .NET platforms.&lt;br /&gt;&lt;br /&gt;Bigtable (http://labs.google.com/papers/bigtable.html) from Google and SimpleDB/S3 are also very interesting alternatives.&lt;br /&gt;&lt;br /&gt;Next time you are working on designing a new system, you owe it to yourself to at least check out some alternatives.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-1433822055394674792?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/1433822055394674792/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=1433822055394674792' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/1433822055394674792'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/1433822055394674792'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/06/parallels-between-oil-and-relational.html' title='The parallels between Oil and Relational Database Management Systems'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-734195773111742577.post-8096500162008840743</id><published>2008-05-29T07:20:00.000-07:00</published><updated>2008-05-29T07:28:35.312-07:00</updated><title type='text'>Why GWT matters - part I</title><content type='html'>I have been heads down taking a deep look at GWT of late. This is the first installment of my thoughts on why I believe GWT matters.&lt;br /&gt;&lt;div&gt; Before I got into web development, I was heavily involved in GUI development mostly for UNIX workstations. Back in the day, I used toolkits based on Xt - first OLIT and then later Motif (when OLIT was dropped). At about this point, C++ really started to become popular and later on, for another project I used Rogue-Wave Tools.h++.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;The programming model was fairly natural and consisted largely of creating components (or widgets) such as buttons, text fields, sliders, scroll bars and the like; adding them to other components such as panels or frames; specifying properties such as colour, size, font; organizing into a layout; adding callbacks (or listeners) to respond to events (e.g. button pushed, slider dragged).&lt;/div&gt;&lt;br /&gt;&lt;div&gt;All components were created in the same way, although some components naturally exposed different properties you could set.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;This standardized programming model laid the foundation for impressive tools such as Visual Basic and Borland Delphi. Back in the day, the buzzword of the day was RAD or Rapid Application Development. These tools, enabled a blank canvas representing the application under construction on which you could drag and drop components onto the canvas from a palette of available components. Behind the scenes, code was being generated to reflect the visual design.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;And then, the web happened. Once it got past the initial "brochure-ware" stage, thanks initially to CGI it quickly evolved into an application platform. The web was great in that it truly solved the distribution problem. You no longer had to worry about getting your application installed. Instead, you would drop it on the server and the user would just point their browser and boom you're away. Upgrades - not a problem. Who cares what platform the browser runs on - you no longer had to worry about porting code natively to the platform. As long as the browser was available on the platform, your application was accessible. &lt;/div&gt;&lt;br /&gt;&lt;div&gt; However, this came at some cost. We had to learn new stuff like HTML, HTTP and unlearn some old stuff.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Read the next installment - part II (when I get around to writing it)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/734195773111742577-8096500162008840743?l=www.jasondchambers.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.jasondchambers.com/feeds/8096500162008840743/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=734195773111742577&amp;postID=8096500162008840743' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/8096500162008840743'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/734195773111742577/posts/default/8096500162008840743'/><link rel='alternate' type='text/html' href='http://www.jasondchambers.com/2008/05/why-gwt-matters-part-i.html' title='Why GWT matters - part I'/><author><name>Jason Chambers</name><uri>http://www.blogger.com/profile/10367857945070351431</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://bp1.blogger.com/_SIJ9fKIpUKU/SItHR0u1ttI/AAAAAAAAAAM/z3BvbLeOMD0/S220/profile.png'/></author><thr:total>0</thr:total></entry></feed>
