tag:blogger.com,1999:blog-38211152215714600382024-02-19T02:10:02.497-08:00Shanks' Tech MusingsShankar vasudevanhttp://www.blogger.com/profile/11042051866246691039noreply@blogger.comBlogger8125tag:blogger.com,1999:blog-3821115221571460038.post-17112529431347241862015-08-18T12:24:00.000-07:002015-08-18T12:24:19.935-07:00Thoughts on Architecture and Software Design<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="background-color: white; color: #333333; font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-size: 18px; line-height: 26.1000003814697px;">To be honest and fair, these are not my thoughts but great principles I have learnt in my programming career. Its a unassorted list of references that I keep going back to, when discussing architectural concepts with development teams. Penning it here as my own reference.</span><br />
<h3 id="serviceorienteddesign" style="background-color: white; color: #333333; cursor: default; font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-size: 1.4em; font-weight: 300; line-height: 1.1em; margin: 1em 0px 0.6em; padding: 0px;">
Service Oriented Design</h3>
<div style="background-color: white; color: #333333; font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-size: 18px; line-height: 1.45em; margin-bottom: 17.6px; padding: 0px;">
Services are the building blocks of architecture. They need to be simple, self contained and designed for re-use; right from the start. Application features expose their data and functionality through service interfaces. Services communicate with each other only through the interface over the network (Http / Messaging). No other communication allowed - binary dependency, direct data store access, shared memory model and all other backdoor access should be disallowed. All services should be designed to be externalizable.</div>
<div style="background-color: white; color: #333333; font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-size: 18px; line-height: 1.45em; margin-bottom: 17.6px; padding: 0px;">
Ref: <a href="https://plus.google.com/app/basic/stream/z12ld3fwhlnexv5b004cjv0oapmuflzrezc0k" style="color: #448ac9; cursor: pointer; margin: 0px; padding: 0px; text-decoration: none;">https://plus.google.com/app/basic/stream/z12ld3fwhlnexv5b004cjv0oapmuflzrezc0k</a></div>
<h3 id="designparadigms" style="background-color: white; color: #333333; cursor: default; font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-size: 1.4em; font-weight: 300; line-height: 1.1em; margin: 1em 0px 0.6em; padding: 0px;">
Design Paradigms</h3>
<div style="background-color: white; color: #333333; font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-size: 18px; line-height: 1.45em; margin-bottom: 17.6px; padding: 0px;">
<b style="margin: 0px; padding: 0px;">API Driven</b> - Abstraction is the key component of providing an integrated service offering. Applications needs to be built on top of a rich API which assists down the line with integration with downstream and upstream systems and implementations can be changed without impact.</div>
<div style="background-color: white; color: #333333; font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-size: 18px; line-height: 1.45em; margin-bottom: 17.6px; padding: 0px;">
<b style="margin: 0px; padding: 0px;">Scalable</b> - Applications should be decomposed into small, loosely coupled, stateless building blocks that can be scaled independently. Architecture should always bear cost in mind and allow business levers to scale both horizontally and vertically as necessary.</div>
<div style="background-color: white; color: #333333; font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-size: 18px; line-height: 1.45em; margin-bottom: 17.6px; padding: 0px;">
<b style="margin: 0px; padding: 0px;">Adaptive</b> - Dont make applications more complicated than they should ever be. Follow the KISS principle. Look for Apple product design guidelines as an inspiration to make things as intuitive as possible.</div>
<div style="background-color: white; color: #333333; font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-size: 18px; line-height: 1.45em; margin-bottom: 17.6px; padding: 0px;">
<b style="margin: 0px; padding: 0px;">Resilient</b> - Protecting your organization's / customer's data is the first priority for applications. Applications must be able to absorb individual failures and provide a seamless degradation of service based on availability. Don't treat failure as an exception but as an event of regular happening.</div>
<div style="background-color: white; color: #333333; font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-size: 18px; line-height: 1.45em; margin-bottom: 17.6px; padding: 0px;">
<b style="margin: 0px; padding: 0px;">Secure</b> - Integrating security into applications should be a ground-up activity and not an afterthought. Enterprise level of authentication, access-controls and data encryption should be applied from basic design.</div>
<div style="background-color: white; color: #333333; font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-size: 18px; line-height: 1.45em; margin-bottom: 17.6px; padding: 0px;">
Ref:<br style="margin: 0px; padding: 0px;" /><a href="http://www.infoq.com/presentations/Embracing-Uncertainty" style="color: #448ac9; cursor: pointer; margin: 0px; padding: 0px; text-decoration: none;">Embracing uncertainty</a><br style="margin: 0px; padding: 0px;" /><a href="http://vimeo.com/43612849" style="color: #448ac9; cursor: pointer; margin: 0px; padding: 0px; text-decoration: none;">Clean architecture</a><br style="margin: 0px; padding: 0px;" /><a href="http://www.somethingsimilar.com/2013/01/14/notes-on-distributed-systems-for-young-bloods/" style="color: #448ac9; cursor: pointer; margin: 0px; padding: 0px; text-decoration: none;">Distributed Systems</a><br style="margin: 0px; padding: 0px;" /><a href="http://blog.jooq.org/2013/03/30/how-to-design-a-good-regular-api/" style="color: #448ac9; cursor: pointer; margin: 0px; padding: 0px; text-decoration: none;">Designing a good API</a><br style="margin: 0px; padding: 0px;" /><a href="http://techblog.netflix.com/2011/07/netflix-simian-army.html" style="color: #448ac9; cursor: pointer; margin: 0px; padding: 0px; text-decoration: none;">Design for failure - The Simian Army</a><br style="margin: 0px; padding: 0px;" /><a href="http://martinfowler.com/bliki/FrequencyReducesDifficulty.html" style="color: #448ac9; cursor: pointer; margin: 0px; padding: 0px; text-decoration: none;">Frequency reduces difficulty</a></div>
<h3 id="continuousdelivery" style="background-color: white; color: #333333; cursor: default; font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-size: 1.4em; font-weight: 300; line-height: 1.1em; margin: 1em 0px 0.6em; padding: 0px;">
Continuous Delivery</h3>
<div style="background-color: white; color: #333333; font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-size: 18px; line-height: 1.45em; margin-bottom: 17.6px; padding: 0px;">
<b style="margin: 0px; padding: 0px;">Principles vs Practices</b> - Best practices are subjective and depend largely on context, while principles are eternal and universal. The delivery lifecycle should be based on fundamental 'agile manifesto' rather than beating around on specific development practices. - <a href="http://simpleprogrammer.com/2013/02/17/principles-are-timeless-best-practices-are-fads/" style="color: #448ac9; cursor: pointer; margin: 0px; padding: 0px; text-decoration: none;">http://simpleprogrammer.com/2013/02/17/principles-are-timeless-best-practices-are-fads/</a></div>
<div style="background-color: white; color: #333333; font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-size: 18px; line-height: 1.45em; margin-bottom: 17.6px; padding: 0px;">
<b style="margin: 0px; padding: 0px;">Continuous business value vs Time-boxed iterations</b> - The delivery should be based on providing continuous business value in the form of small functional releases rather than arbitrary time boxes. - <a href="http://kief.com/iterations-considered-harmful.html" style="color: #448ac9; cursor: pointer; margin: 0px; padding: 0px; text-decoration: none;">http://kief.com/iterations-considered-harmful.html</a></div>
<div style="background-color: white; color: #333333; font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-size: 18px; line-height: 1.45em; margin-bottom: 17.6px; padding: 0px;">
<b style="margin: 0px; padding: 0px;">Measurements and analytics</b> - You really have to treat the software development process more like a relationship than like a factory. Instead of focussing on measurements and metrics, the emphasis should be given to trends and variance of the development lifecycle. - <a href="http://simpleprogrammer.com/2013/02/11/we-cant-measure-anything-in-software-development/" style="color: #448ac9; cursor: pointer; margin: 0px; padding: 0px; text-decoration: none;">http://simpleprogrammer.com/2013/02/11/we-cant-measure-anything-in-software-development/</a></div>
<div style="background-color: white; color: #333333; font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-size: 18px; line-height: 1.45em; margin-bottom: 17.6px; padding: 0px;">
<b style="margin: 0px; padding: 0px;">Philosophy on Estimations</b> - The key is that you first accept that making accurate long-term estimates is fundamentally impossible. - <a href="http://blog.hut8labs.com/coding-fast-and-slow.html" style="color: #448ac9; cursor: pointer; margin: 0px; padding: 0px; text-decoration: none;">http://blog.hut8labs.com/coding-fast-and-slow.html</a></div>
<div style="background-color: white; color: #333333; font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; font-size: 18px; line-height: 1.45em; margin-bottom: 17.6px; padding: 0px;">
<br /></div>
</div>
Shankar vasudevanhttp://www.blogger.com/profile/11042051866246691039noreply@blogger.com7tag:blogger.com,1999:blog-3821115221571460038.post-44210300490485758282015-01-15T11:14:00.001-08:002015-01-15T11:26:07.199-08:00Simple Conferencing with Twilio, BlockSpring and AWS<div dir="ltr" style="text-align: left;" trbidi="on">
Recently an entrepreneur friend of mine complained that plain teleconferencing services are expensive and in certain business situations, it is not possible to use social applications like Skype or Google Hangouts. To quantify it, he said an hour call between 4 people in the US and UK cost around ~ GBP 15 or around $23 at professional providers like GotoMeeting or Powwownow. Having known about <a href="http://www.twilio.com/" target="_blank">Twilio</a> and the power of Cloud, I felt this was not right and set about to write a simple conferencing facility POC for him which turned to be a huge cost saver. Interestingly a TollFree number costs about $2 per month and a local number about 1$, that comes to around 4$ per month recurring or admin cost as he decided to have a TollFree and Local number in the US and just a local number in the UK. A similar conference for 4 people now only costed $2.5 per hour and it was super efficient, also when we added a web based calling to it, the cost flattened around a dollar or less.<br />
<br />
One of the salient features of the POC was that I decided not to build any backend application and use <a href="http://api.blockspring.com/" target="_blank">BlockSpring</a> one of the recent discoveries for me to build a function that can allow to integrate Twilio. And use only Amazon AWS S3 to host a static web page which is a client-side Javascript application as the end point. This post simply details the steps and the code that was used to build the POC and make it open to anyone who would like to try on their own.<br />
<br />
<div>
<div>
<h4>
1. Create a static website using Amazon S3</h4>
<div>
You can refer to the <a href="http://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteHosting.html" target="_blank">official guide</a> or follow this <a href="http://davidwalsh.name/hosting-website-amazon-s3" target="_blank">simple tutorial</a> to create a static website on Amazon S3. Also you can setup a custom domain name like simple-conference.mydomain.com if that is something you'd prefer.<br />
<br /></div>
<h4>
2. Register two applications with Twilio</h4>
</div>
</div>
<div>
Sign-up for a new account at Twilio if you already did not have one and do spend sometime looking at the self-explanatory guides and documentation which are very useful. For the conferencing app, we will create two application end-points, one to act as an administrator account which would begin and end the conference and the second one adds users as participants to the call.<br />
<br />
<br /></div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_oDjpbz10o8ErJPuaDBy3odnDXpJPloykuXdPuziqPCNPZXx1TMBSaxYGpzn8bUpy6Y8jqMrD1a_bBm1ihale9w4NNzYW14GzmlJrNAp9rBdixoFXBa76LTUrKT6TaMMp0PziTeCyCioW/s1600/Screen+Shot+2015-01-15+at+18.35.45.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_oDjpbz10o8ErJPuaDBy3odnDXpJPloykuXdPuziqPCNPZXx1TMBSaxYGpzn8bUpy6Y8jqMrD1a_bBm1ihale9w4NNzYW14GzmlJrNAp9rBdixoFXBa76LTUrKT6TaMMp0PziTeCyCioW/s1600/Screen+Shot+2015-01-15+at+18.35.45.png" height="336" width="640" /></a></div>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6kP83BTLz_AOqOKEFiWR3c7U49bpKnW7qBJg7OJ63usRVO4eBIl3JY6c8-e0wpdojVP0Z6UyXx55_g6Vas7OHkYcHeAxHCzCq9aeyJUQlr8YzLFaiAgw2NXwaZzOrpnodDw1w-drmXSeY/s1600/Screen+Shot+2015-01-15+at+18.41.59.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6kP83BTLz_AOqOKEFiWR3c7U49bpKnW7qBJg7OJ63usRVO4eBIl3JY6c8-e0wpdojVP0Z6UyXx55_g6Vas7OHkYcHeAxHCzCq9aeyJUQlr8YzLFaiAgw2NXwaZzOrpnodDw1w-drmXSeY/s1600/Screen+Shot+2015-01-15+at+18.41.59.png" height="334" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
NOTE: Please remember that we are using Amazon S3 to host our TwiML end-points, so the request URL must have the <i><span style="font-family: Courier New, Courier, monospace;">"HTTP GET"</span></i> method, otherwise you'd have exceptions.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<h4 style="clear: both; text-align: left;">
3. Add the following code end-points to S3 website</h4>
<div>
<script src="https://gist.github.com/vshank77/dc348be62e381d2f9e89.js"></script></div>
<h4 style="clear: both; text-align: left;">
4. Create an account at BlockSpring</h4>
<div class="separator" style="clear: both; text-align: left;">
BlockSpring allows developers to create, run and share functions across multiple server side languages including PHP, Python, NodeJs, Ruby, R etc. Though the following function is open-source and can be called from your application, you will still need to register an account and get an API Key of your own to connect to the function. </div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<h4 style="clear: both; text-align: left;">
5. Use the Twilio Token Generator Function from BlockSpring</h4>
<div class="separator" style="clear: both; text-align: left;">
You can use my public open-source function hosted at <a href="https://www.blogger.com/%22https://api.blockspring.com/vshank77/82c3cb17747dcc677ab5816969962f91%22">"https://api.blockspring.com/vshank77/82c3cb17747dcc677ab5816969962f91"</a>.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLsWrPmQP3UrwHk_sslkQVAift34GTvN2_1QeeLE2jbdtk9Dnm0FJtA9SF60hzkq_z2KdnHHy4pxWG6dAHKqG9MnWYeEccer0yGBlp-Z2PNj8N1qg4p_3ZA2YcxCaKK6XFXZJJs63fHfml/s1600/Screen+Shot+2015-01-15+at+19.01.59.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLsWrPmQP3UrwHk_sslkQVAift34GTvN2_1QeeLE2jbdtk9Dnm0FJtA9SF60hzkq_z2KdnHHy4pxWG6dAHKqG9MnWYeEccer0yGBlp-Z2PNj8N1qg4p_3ZA2YcxCaKK6XFXZJJs63fHfml/s1600/Screen+Shot+2015-01-15+at+19.01.59.png" height="355" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Since at the time of this writing, there is no integration between Github GIST and BlockSpring or way to embed a BlockSpring function, I have elaborated the source code of the Twilio.</div>
<br />
<script src="https://gist.github.com/vshank77/55f1b05d426ff0080c35.js"></script>
<br />
<h4 style="clear: both; text-align: left;">
6. Create the Javascript App and add it your S3 website</h4>
<div style="clear: both; text-align: left;">
Please note that the account ID / Application ID in the following code are hard-coded to test accounts and will not work. You will need to use your own Twilio Account ID and Begin/Join App_ID when you upload this code.<br />
<br /></div>
<div>
<script src="https://gist.github.com/vshank77/991598f76e928483f5d5.js"></script></div>
<h4 style="clear: both; text-align: left;">
7. Optionally associate calling numbers to the Twilio Function</h4>
<div class="separator" style="clear: both; text-align: left;">
If you also need to connect to the application from regular phone number, buy as many numbers as you want and point them to the "Simple Conference Join" TwiML function. The only caveat is that since we don't have any backend service of our own, you can create administrator functions on the phone and will have to use web site to begin the conference.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
That's it; You now have a fully functioning conference at a very low cost.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div>
<br /></div>
</div>
Shankar vasudevanhttp://www.blogger.com/profile/11042051866246691039noreply@blogger.com0tag:blogger.com,1999:blog-3821115221571460038.post-35841900572343774432012-11-30T06:46:00.001-08:002012-11-30T06:52:01.212-08:00Hard reverting a Maven release with Git<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">When trying to release a maven project, Git automatically tags your repository and then updates the project to a new version. However if there are any failures in between, the release cannot be fully undone and needs manual intervention to clean up the state.</span><br />
<div>
<span style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br /></span></div>
<div>
<span style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">The following code snippet shows how you can fully revert your project by removing both the tags and resetting the previous commit.</span></div>
<script src="https://gist.github.com/3012876.js"></script>
</div>Shankar vasudevanhttp://www.blogger.com/profile/11042051866246691039noreply@blogger.com0tag:blogger.com,1999:blog-3821115221571460038.post-11928421495987182122012-11-29T10:47:00.002-08:002012-11-29T10:47:52.879-08:00New Age Architecture principles learnt from AWS re: Invent keynote<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="background-color: white;"><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Was listening to the Keynote from Werner Vogels at A</span><span style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><span style="color: #333333; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">WS </span><a href="http://reinvent.awsevents.com/" target="_blank">re: Invent</a><span style="color: #333333; font-size: 14px; line-height: 22px; text-align: -webkit-auto;"> and</span></span><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;"> wanted to record all the concepts that he discussed. These are so valuable and applicable for large applications in today's environment and worth blogging about.</span><br style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: -webkit-auto;" /><br style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: -webkit-auto;" /><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Fundamental Principle - <i>EVERYTHING IS A PROGRAMMABLE RESOURCE</i> </span><br style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: -webkit-auto;" /><br style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: -webkit-auto;" /><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;"><u><b>Controllable</b></u></span></span><br />
<ul style="text-align: left;">
<li><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Decompose into small, loosely coupled, stateless building blocks </span></li>
<li><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Automate processes and services</span></li>
<li><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Let business levers control the system</span></li>
<li><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Architect with cost in mind</span></li>
</ul>
<span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;"><b><u>Resilient</u></b></span><br />
<br />
<ul style="text-align: left;">
<li><span style="background-color: white; color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Protecting your customer is the first priority</span></li>
<li><span style="background-color: white; color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">In production, deploy to at least two availability zones</span></li>
<li><span style="background-color: white; color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Integrate security into your application from the group up</span></li>
<li><span style="background-color: white; color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Build, test, integrate and deploy continuously</span></li>
<li><span style="background-color: white; color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Don't think in single failures</span></li>
<li><span style="background-color: white; color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Don't treat failure as an exception</span></li>
</ul>
<span style="background-color: white;"><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;"><b><u>Adaptive</u></b></span><br style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: -webkit-auto;" /><ul style="text-align: left;">
<li><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Entla non sunt multiplicanda praeter necessitiatem - dont make things more complicated than it should be </span></li>
<li><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Assume Nothing</span></li>
<li><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Use late binding</span></li>
</ul>
<span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;"><b><u>Data Driven</u></b></span><br style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: -webkit-auto;" /><ul style="text-align: left;">
<li><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Instrument everything, all the time</span></li>
<li><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Inspect the whole distribution</span></li>
<li><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Put everthing in logs</span></li>
</ul>
</span><br />
<span style="background-color: white;"><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;"><br /></span></span>
<span style="background-color: white;"><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">Some of the quotable quotes from the session include</span></span><br />
<span style="background-color: white;"><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;"><br /></span></span>
<span style="background-color: white;"><i><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">"you are allowed to make mistakes"</span><br style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: -webkit-auto;" /><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">"if you cannot collect data, you cannot act"</span><br style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: -webkit-auto;" /><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">"whole distribution not average or median - inspect 99.9 percentile"</span><br style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: -webkit-auto;" /><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;">"Thou shalt turn off the lights"</span></i></span><br />
<span style="background-color: white;"><span style="color: #333333; font-family: 'Helvetica Neue', Arial, Helvetica, Arimo, FreeSans, 'Nimbus Sans', 'Phetsarath OT', Malayalam, 'Gargi_1.7', sans-serif; font-size: 14px; line-height: 22px; text-align: -webkit-auto;"><br /></span></span></div>
Shankar vasudevanhttp://www.blogger.com/profile/11042051866246691039noreply@blogger.com0tag:blogger.com,1999:blog-3821115221571460038.post-50522055507399577352012-06-28T09:37:00.000-07:002012-06-28T09:37:29.032-07:00Introducing Polyglotted<div dir="ltr" style="text-align: left;" trbidi="on">
<p>My name is Shankar Vasudevan, a developer and architect, working for a leading
investment bank within the London city. I have been architecting large scale
enterprise applications for the past 15 years. You can visit my <a href="http://uk.linkedin.com/in/vshank77">professional
profile</a> on LinkedIn or follow me
<a href="http://twitter.com/#!/vshank77">@vshank77</a> on Twitter.</p>
<p>Over the past 15 years, I have grown a great fascination for data storage and
analysis. The advent of Big Data and NoSQL in the recent years have got me
drooling and my professional work in the fields of information retrieval,
analytical processing, adword classification and scoring, enterprise search
have added fuel to the fire. More recently I have become a true believer of
<a href="http://martinfowler.com/bliki/PolyglotPersistence.html">Polyglot Persistence</a>
a keyword coined by <a href="www.martinfowler.com">Martin Fowler</a> and apply the
philosophy in day-to-day job.</p>
<p>Polyglotted.org is to give back to the community, projects that I conceive
in the area of information analysis and polyglot persistence.</p>
<p>If you are still interested, Martin Fowler has an interesting introduction
to <a href="http://martinfowler.com/%0Aarticles/nosql-intro.pdf">NoSQL Databases and Polyglot Persistence</a></p>
<br />
Cross-posted from <a href="http://www.polyglotted.org/2012/06/10/introducing-polyglotted.html">Polyglotted Blog</a>
</div>Shankar vasudevanhttp://www.blogger.com/profile/11042051866246691039noreply@blogger.com0tag:blogger.com,1999:blog-3821115221571460038.post-90381564027015736042010-02-01T13:46:00.000-08:002012-11-29T10:49:39.428-08:00JMockit & JavaAgent - Enterprise integration woesWe've been using <a href="http://code.google.com/p/jmockit/" target="_blank">JMockit</a> as the preferred mocking utility at a large Bank that I'm consulting at. While generally the product and its <a href="http://dhruba.name/2009/11/08/jmockit-no-holds-barred-testing-with-instrumentation-over-mocking/" target="_blank">non-intrusive mocking</a> features are commendable, some of the under-the-hood mechanisms have caused us a nightmare with our continuous integration.After upgrading to latest version of JMockit, version 0.996.0, we had ALL our unit tests failing with the following spurious error<br />
<pre class="brush: java">java.lang.NoClassDefFoundError: org.junit.runner.Runner at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59) at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:24) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.<init>(JUnit4TestReference.java:29) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestClassReference.<init>(JUnit4TestClassReference.java:25) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:40) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:30) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:452) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)</init></init></pre>While my colleague who had been advocating the use of the utility asked me for a way to change the order of libraries in the classpath, we smelt rot and started debugging under the hood. After quite a bit of digging around, we found that the actual error was caused by the failure to load the JMockit agent dynamically. The following code is the sequence of calls which JMockit makes to load the agent.<br />
<pre class="brush: java"> public class Startup { // other code omitted public static void verifyInitialization() { if (instrumentation == null) { new AgentInitialization().initializeAccordingToJDKVersion(); } }}public final class AgentInitialization{ public void initializeAccordingToJDKVersion() { String jarFilePath = discoverPathToJarFile(); if (Startup.jdk6OrLater) { new JDK6AgentLoader(jarFilePath).loadAgent(); } else if ("1.5".equals(Startup.javaSpecVersion)) { throw new IllegalStateException( "JMockit has not been initialized. Check that your Java 5 VM has been started " + "with the -javaagent:" + jarFilePath + " command line option."); } else { throw new IllegalStateException("JMockit requires a Java 5 VM or later."); } } private String discoverPathToJarFile() { CodeSource codeSource = AgentInitialization.class.getProtectionDomain().getCodeSource(); if (codeSource == null) { return findPathToJarFileFromClasspath(); } URI jarFileURI; // URI is needed to deal with spaces and non-ASCII characters try { jarFileURI = codeSource.getLocation().toURI(); } catch (URISyntaxException e) { throw new RuntimeException(e); } return new File(jarFileURI).getPath(); } private String findPathToJarFileFromClasspath() { String[] classPath = System.getProperty("java.class.path").split(File.pathSeparator); for (String cpEntry : classPath) { if (cpEntry.matches(".*jmockit[-.\\d]*.jar")) { return cpEntry; } } return null; }}</pre>While this approach is non-intrusive and works out of the box in most cases, in large enterprise organisations, you are often limited extensively and work in a confined environment. My current environment loads files from a network file system and the following lines from the <i>discoverPathToJarFile()</i> cause the class initialization error<br />
<pre class="brush: java"> jarFileURI = codeSource.getLocation().toURI(); </pre>returns <br />
<pre class="brush: java"> CodeSourcefile://remoterepository/libraries/jmockit/0.996.0/install/common/lib/jmockit.jar </pre>and <br />
<pre class="brush: java"> return new File(jarFileURI).getPath(); </pre>returns <br />
<pre class="brush: java"> IllegalArgumentException: URI has an authority component </pre>We had been using JMockit for a while now since 0.991.0 version and the agent loading had always been a problem. In our original usage, we had to do two things. We had to create our own version of the <a href="http://code.google.com/p/jmockit/source/browse/trunk/main/src/mockit/internal/startup/JDK6AgentLoader.java" target="_blank">JDK6AgentLoader</a> as this class has package level scope and create our own JUnit4 runner which loaded the agent from a hard-coded location, as below<br />
<pre class="brush: java">public final class MyJMockitRunner extends BlockJUnit4ClassRunner{ static { MyJDK6AgentLoader.loadAgent(hardCodedPathtoAgentJar); } public JMockit(Class testClass) throws InitializationError { super(testClass); }}</pre><p><b>Why the problem now?</b><br />
What changed majorly between the two versions was, Shadowing the org.junit.runner.Runner class which in the modified version loaded the java agent before initialization of the class. <br />
<pre class="brush: java">public abstract class Runner implements Describable{ static { Startup.initializeIfNeeded(); } public abstract Description getDescription(); public abstract void run(RunNotifier notifier); public int testCount() { return getDescription().testCount(); }}</pre>So even though we are trying to load the agent in our custom Runner, the loading of the Runner class itself preceding the sequence and aggressively loads the agent in its own strategy.While this is a possible approach, I would highly criticize the usage of class shadowing for the following reasons<ol><li>Open source API generally provide certain basic guarantees, and is generally tested extensively by the commnuity in different enviroments to ensure they work in the way they are designed. By shadowing a public class, you override this guarantee and for most common developers, the fact that they are using a modified class is oblivious. This can lead to late night debugging sessions and nightmares when things go wrong.</li><li>In this particular case, while only the tests that used JMockit should have failed, thousands of test cases across multiple projects failed at once. As a programmer, I always believe to lazily load resources and only when they are needed. But there is a necessity for mocking or not, the agent is going to be loaded whenever a Unit test is run. This is fundamentally wrong.</li><li>Any workarounds that had been applied to the library fail miserably with this aggressive approach to force your library to behave in a specific way.</li><li>While I agree that the owner cannot foresee all the environments his library would be used, trying to perform gimmicks under the hood on an open library could have been avoided.</li></ol><p><b>Solution</b> <br />
While I've criticized the usage of shadowing a class, unfortunately the only solution that worked for us was to shadow the JMockit class that loaded the agent as below.<br />
<pre class="brush: java">public final class AgentInitialization{ public void initializeAccordingToJDKVersion() { MyJDK6AgentLoader.loadAgent(hardCodedPathtoAgentJar); }}</pre><br />
<b>Better approach</b><br />
There are a couple of suggestions for the JMockit API to overcome the shortcomings described above<ol><li>Please do remove the shadowed JUnit class and make the agent loading explicit. This is already the case when you use the JMockit.class as the runner.</li><li>While the auto detection of the agent is brilliant, there could be some hooking mechanism which allowed the developers to extend it with their own strategies.</li></ol>Last but not least, there definitely was an obvious solution to the problem, which was to explicitly specify the agent path in the VM arguments. The issue we had in that was there were too many OS, at least a few IDE, a shared continuous integration server and many many ant build scripts that need to be updated. It would have been a far more involved effort to get all this modified and maintained than applying a code fix which worked "under the hood"Shankar Vasudevanhttp://www.blogger.com/profile/15139006072777819989noreply@blogger.com2tag:blogger.com,1999:blog-3821115221571460038.post-41207468737193266152010-01-17T11:51:00.000-08:002010-01-18T14:05:10.458-08:00Google Appengine and Maven - another hack for datanucleus pluginAfter researching for mavenizing an Appengine pet project, most of the links that Google search returned were really old when first version of AppEngine was released and did not match the expectations. Also unfortunately there is really no maven plugin that supports the latest version as Google publishes them or a plugin that allows you to point to a local installation, so that you could update at the speed of Google releasing updates for AppEngine (as of this writing, there were atleast 3 updates in the shorter period of 2 weeks)
<p />
Thanks to <a href="http://www.salientpoint.com/blog/?p=480" target="_blank">Salient Point</a>, this was the best article to configure a maven project and it allowed to configure any AppEngine version with the simplicity of changing a couple of variables in a script file and building the maven project. However the only caveat that needed a few hours of debugging was the <i>maven-datanucleus-plugin</i>, as again the versions of the Jar files provided by datanucleus repository and the ones that are published by Google were slightly different and it was more simpler to use the same philosophy of having all the Jars provided by Google rather than downloading them from a different repository.
<p />
On this note, the following are the only deviation of configurations from the original post
1) Define the local directory to which the AppEngine Jars have been downloaded, the preferred way is to use profiles such that we could specify a default location in each platform (windows / mac)
<br />
<pre class="brush: xml"> <profile>
<id>gwt-dev-windows</id>
<properties>
<platform>windows</platform>
<appengine.sdk.root>C:\Java\appengine-java-sdk-${appengineVersion}</appengine.sdk.root>
</properties>
<activation>
<activeByDefault>false</activeByDefault>
<os>
<family>windows</family>
</os>
</activation>
</profile>
</pre>
2) Specify a antrun task that performs the datanucleus enhancement for entity objects rather than the datanucleus plugin
<br />
<pre class="brush: xml"> <plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>datanucleus-enhance</id>
<phase>process-classes</phase>
<configuration>
<tasks>
<property name="appengine.tools.classpath"
location="${appengine.sdk.root}/lib/appengine-tools-api.jar"/>
<property name="dependency_classpath" refid="maven.dependency.classpath"/>
<taskdef name="enhance" classname="com.google.appengine.tools.enhancer.EnhancerTask"
classpath="${appengine.tools.classpath}"/>
<enhance failonerror="true" api="JPA">
<classpath>
<pathelement path="${appengine.tools.classpath}"/>
<pathelement path="${dependency_classpath}"/>
<pathelement path="target/classes"/>
</classpath>
<fileset dir="target/classes" includes="**/*.class"/>
</enhance>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</pre>
Alas it works like magic for us and we are able to upgrade the AppEngine versions as and when they are released by Google.
<p />
Also to note is the link on configuring a <a href="http://mojo.codehaus.org/gwt-maven-plugin/user-guide/multiproject.html" target="_blank">multi-project setup</a> that is documented on the maven-gwt-plugin documentation that helps to organize your code into multiple projects (separating the core API / UI / service implementation and AppEngine deployment). Refer to our sample project setup that is hosted at <a href="http://code.google.com/p/scrumsp/source/browse/">http://code.google.com/p/scrumsp</a><br />Shankar Vasudevanhttp://www.blogger.com/profile/15139006072777819989noreply@blogger.com0tag:blogger.com,1999:blog-3821115221571460038.post-92088682221807764432009-12-15T22:49:00.000-08:002010-01-18T14:06:55.131-08:00Builder Pattern Abused<span style="font-family: 'Lucida Grande'; font-size: 13px; line-height: 19px; white-space: pre-wrap;">Everyone has read Joshua Bloch's builder pattern, which he has also described at his <a href="http://developers.sun.com/learning/javaoneonline/j1sessn.jsp?sessn=TS-2689&yr=2007&track=5">"Effective Java Reloaded" </a>sessions at Javaone. However in this post, I am going to describe a misuse of this pattern, when used in conjunction with inheritance. </span><br />
<span style="font-family: 'Lucida Grande'; font-size: 13px; line-height: 19px; white-space: pre-wrap;">When building a relational model, the biggest challenge is to maintain the integrity of inheritance and reduce the visibility of the properties completely. However if you need builders for this model, then you either have to create a class hierarchy of builders similar to the model itself or make all accesses protected (ugly #1) so that builders could appropriately initialize them. Making the properties less visible has at least two disadvantages though (1) you cannot make them final and ensure construction safe and (2) you'll end up copying the builders to more than one class (ugly #2)
I recently came across a horrendous implementation of the hierarchy of builders where the author could not solve the above issues elegantly. So he had resorted to creating copy constructors (ugly #3) for the builders and also duplicating the methods for each of the builders.
The following code snippet exactly reproduces the code, in an example class hierarchy and its builders.</span>
<br />
<pre class="brush: java">abstract class Shape {
private String name;
private Dimension dimension;
public enum Dimension {
TWO_D, THREE_D;
}
public static abstract class ShapeBuilder {
private String name;
private Dimension dimension;
protected ShapeBuilder copyOf(ShapeBuilder builder) {
ShapeBuilder copy = createBuilder();
copy.name = builder.name;
copy.dimension = builder.dimension;
return copy;
}
public ShapeBuilder name(String name) {
ShapeBuilder bldr = copyOf(this);
bldr.name = name;
return bldr;
}
public ShapeBuilder dimension(Dimension dimension) {
ShapeBuilder bldr = copyOf(this);
bldr.dimension = dimension;
return bldr;
}
public Shape build() {
Shape s = createInstance();
s.name = this.name;
s.dimension = this.dimension;
return s;
}
abstract Shape createInstance();
abstract ShapeBuilder createBuilder();
}
// omitting getters for clarity
}
abstract class TwoDShape extends Shape {
private double area;
public static abstract class TwoDShapeBuilder extends ShapeBuilder {
private double area;
protected TwoDShapeBuilder copyOf(TwoDShapeBuilder builder) {
TwoDShapeBuilder copy = (TwoDShapeBuilder) super.copyOf(builder);
copy.area = this.area;
return copy;
}
public TwoDShapeBuilder name(String name) {
return (TwoDShapeBuilder) super.name(name);
}
public TwoDShapeBuilder dimension(Dimension dimension) {
return (TwoDShapeBuilder) super.dimension(dimension);
}
public TwoDShapeBuilder area(double area) {
TwoDShapeBuilder bldr = copyOf(this);
bldr.area = area;
return bldr;
}
public TwoDShape build() {
TwoDShape s = (TwoDShape) super.build();
s.area = this.area;
return s;
}
}
}
class Circle extends TwoDShape {
private double radius;
public static class CircleBuilder extends TwoDShapeBuilder {
private double radius;
protected CircleBuilder copyOf(CircleBuilder builder) {
CircleBuilder copy = (CircleBuilder) super.copyOf(builder);
copy.radius = this.radius;
return copy;
}
public CircleBuilder name(String name) {
return (CircleBuilder) super.name(name);
}
public CircleBuilder dimension(Dimension dimension) {
return (CircleBuilder) super.dimension(dimension);
}
public CircleBuilder area(double area) {
return (CircleBuilder) super.area(area);
}
public CircleBuilder radius(double radius) {
CircleBuilder bldr = copyOf(this);
bldr.radius = radius;
return bldr;
}
public Circle build() {
Circle s = (Circle) super.build();
s.radius = this.radius;
return s;
}
@Override
protected Shape createInstance() {
return new Circle();
}
@Override
protected ShapeBuilder createBuilder() {
return new CircleBuilder();
}
}
}
abstract class ThreeDShape extends Shape {
private double volume;
// builder similar to 2DShape
}
class Cube extends ThreeDShape {
private int side;
// builder similar to Circle
}
</pre>
<span style="font-family: 'Lucida Grande'; font-size: 13px; line-height: 19px; white-space: pre-wrap;">Now extrapolate this class into an enterprise relational model consisting of 10-15 objects with nested hierarchies and you can imagine the amount of stupid code that's been written.
If you haven't solved this problem yourself before, please stop here and think for a solution. The biggest issue here is, when you create a CircleBuilder and call the name() method, you are again expecting a CircleBuilder back and not a ShapeBuilder. Thankfully there is a generic implementation (partly under documented) which helps to solve problems of this nature. The modified code is given below</span>
<br />
<pre class="brush: java">abstract class Shape {
private String name;
private Dimension dimension;
public enum Dimension {
TWO_D, THREE_D;
}
public static abstract class ShapeBuilder<S extends Shape, SB extends ShapeBuilder<S, SB>> {
private String name;
private Dimension dimension;
@SuppressWarnings("unchecked")
public SB name(String name) {
this.name = name;
return (SB) this;
}
@SuppressWarnings("unchecked")
public SB dimension(Dimension dimension) {
this.dimension = dimension;
return (SB) this;
}
public T build() {
T result = createInstance();
result.name = this.name;
result.dimension = this.dimension;
return result;
}
abstract T createInstance();
}
}
abstract class TwoDShape extends Shape {
private double area;
public static abstract class TwoDShapeBuilder<S extends TwoDShape, SB extends TwoDShapeBuilder<S, SB>>
extends ShapeBuilder<T, SB> {
private double area;
@SuppressWarnings("unchecked")
public SB area(double area) {
this.area = area;
return (SB) this;
}
public T build() {
T result = (T) super.build();
result.area = this.area;
return result;
}
}
}
class Circle extends TwoDShape {
private double radius;
public static class CircleBuilder extends
TwoDShapeBuilder<Circle, CircleBuilder> {
private double radius;
public CircleBuilder radius(double radius) {
this.radius = radius;
return this;
}
public Circle build() {
Circle s = (Circle) super.build();
s.radius = this.radius;
return s;
}
@Override
protected Circle createInstance() {
return new Circle();
}
}
}</pre>
<span style="font-family: 'Lucida Grande'; font-size: 13px; line-height: 19px; white-space: pre-wrap;">The only thing ugly about this code is that the builders themselves have to be cast to the generic implementation because of the type erasure. However this would be solved by the new <a href="http://mail.openjdk.java.net/pipermail/coin-dev/2009-February/000009.html">"Type Inference"</a> language feature that will be introduced in Java 7. <br /> </span><br />Shankar Vasudevanhttp://www.blogger.com/profile/15139006072777819989noreply@blogger.com1