<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;C0QNSXcyeCp7ImA9WhRSF00.&quot;"><id>tag:blogger.com,1999:blog-34378650</id><updated>2011-11-19T11:16:38.990Z</updated><category term="ruby" /><category term="c#" /><category term="javaconfig" /><category term="java" /><category term="web services" /><category term="SOA" /><category term="web" /><category term="spring" /><category term="security" /><category term="software development" /><title>Patric Fornasier's Blog</title><subtitle type="html" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://patforna.blogspot.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>42</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/patforna" /><feedburner:info uri="patforna" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;CkMDQ3s_cSp7ImA9WhZREE0.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-2711762515200485577</id><published>2011-04-05T11:36:00.003+01:00</published><updated>2011-04-05T11:47:52.549+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-05T11:47:52.549+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><category scheme="http://www.blogger.com/atom/ns#" term="web" /><title>ContainerLess</title><content type="html">I've recently worked on a project where we built a relatively small web application for a client. There was a hard release date, which gave us less than 4 weeks to build and deliver the application. There were several things we did to ensure that we would be able to release on time. One of the them was making sure very early that we could deploy the new application into a production-like environment. During the first few days, we therefore invested time in building infrastructure to deploy the application automatically and continuously (i.e. after each build) to a production-like environment. Going through this process was useful in several different ways:&lt;ul&gt;&lt;li&gt;it made us think early on in the project, what will be required to run the application in production (e.g. operating system, software, ports, certificates, etc.). This gave us sufficient time to react to unexpected obstacles;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;by automating the deployment process early, we reduced the number of times we had to do this activity manually later on;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;seeing the application work in a production-like environment was encouraging feedback that we would be able to release to production whenever our client required;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;deploying the application while it was still very simple allowed us to focus on the deployment process without getting distracted by problems caused by the application.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Ditching the Container&lt;/h3&gt;&lt;p&gt;When we first tried to deploy the application, it was not much more than a Hello World web application. We decided to use Jetty as a web server, since we had used it many times on projects before - both standalone and embedded. Whenever we used Jetty in the past, however, we would normally build a WAR file or a WAR-like directory structure, write a web deployment descriptor and finally deploy or load the artefact into Jetty.&lt;/p&gt;&lt;p&gt;Having spent a bit of time working on a Ruby on Rails application recently, the deployment process used in the past seemed cumbersome and complicated - even more so given the simplicity of the application at hand. That's when we realised that what we cared most about is the application itself and to a much lesser extent the container. In fact, we didn't care about the container at all. All we cared about was that our application would be able to serve incoming requests. At some point, we started to think of the application more of as a self-contained program, which just happened to expose some of its functionality via HTTP. The diagram below illustrates this mind-shift.&lt;/p&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-K2lhkdG6xjI/TZrwPZm9HmI/AAAAAAAAAJQ/i014GAjqvQI/s1600/container-less.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 265px;" src="http://3.bp.blogspot.com/-K2lhkdG6xjI/TZrwPZm9HmI/AAAAAAAAAJQ/i014GAjqvQI/s400/container-less.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5592046034696871522" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;h3&gt;Consequences&lt;/h3&gt;&lt;p&gt;The biggest change was the mind-shift. It took some time to get used to the idea that the application is no longer "contained" in something else, but that it can be "self-contained". But there were also a number of other practical consequences that this change entailed:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;simplified development process. It was very easy and fast for developers to run the application in the same way as it would run in production;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;simplified testing. It was very easy to start and stop the application for automated functional tests;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;simplified packaging and deployment process (e.g. no web.xml, no war file, no prescribed directory structure, etc.);&lt;/li&gt;&lt;br /&gt;&lt;li&gt;simplified application start (i.e. simply execute a Java program);&lt;/li&gt;&lt;br /&gt;&lt;li&gt;started using standard DI mechanisms to wire up Jetty as a dependency and injected it where needed.*&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;* Strictly speaking, you don't need to ditch the container to do this but we felt that the mind-shift we had gone through enabled us to see the potential for doing this.&lt;/p&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;p&gt;Making this architectural change simplified our development and deployment process. It also simplified the way we thought about the application. The only price we paid was loosing the ability to deploy the application to different web server without making changes. Fortunately, this price was purely hypothetical because we had absolutely no need nor desire to use a different web server in production. On the positive side, we were now hiding the fact that we were relying on Java Servlets to realise our web functionality. In fact, seeing what some of the other communities are building (e.g. Sinatra, Node.js, etc.), I'd be tempted to try to write a web application that doesn't use Servlets at all. Part of it for fun, part of it to overcome some of the limitations inherent with the Servlet model.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-2711762515200485577?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/2711762515200485577/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=2711762515200485577" title="6 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/2711762515200485577?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/2711762515200485577?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/rNCm98rX4yI/containerless.html" title="ContainerLess" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-K2lhkdG6xjI/TZrwPZm9HmI/AAAAAAAAAJQ/i014GAjqvQI/s72-c/container-less.png" height="72" width="72" /><thr:total>6</thr:total><feedburner:origLink>http://patforna.blogspot.com/2011/04/containerless.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0UCQHcyeCp7ImA9Wx5QFEg.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-2101206513132923190</id><published>2010-09-02T21:14:00.002+01:00</published><updated>2010-09-02T21:27:41.990+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-09-02T21:27:41.990+01:00</app:edited><title>Agile Talk in Bern</title><content type="html">Next Monday (September 6th), I'm giving a talk in Bern. I'll be talking about how our software delivery teams around the globe work and get stuff done.&lt;br /&gt;&lt;br /&gt;The talk will start at 6pm at Restaurant Schmiedstube. More info and registration via &lt;a href="http://www.guild42.ch/10601/17722.html"&gt;Guild42&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Come along if you're in the area!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-2101206513132923190?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/2101206513132923190/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=2101206513132923190" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/2101206513132923190?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/2101206513132923190?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/ILDuqfp4flY/agile-talk-in-bern.html" title="Agile Talk in Bern" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://patforna.blogspot.com/2010/09/agile-talk-in-bern.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0ICQ38zcCp7ImA9WxFSEE8.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-1185391616346242799</id><published>2010-04-11T22:08:00.011+01:00</published><updated>2010-04-11T22:32:42.188+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-11T22:32:42.188+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><category scheme="http://www.blogger.com/atom/ns#" term="spring" /><category scheme="http://www.blogger.com/atom/ns#" term="javaconfig" /><title>Using Spring Java Config to wire up Dependencies</title><content type="html">The following tests demonstrate how, in &lt;a target="_blank" href="http://www.springsource.org/javaconfig"&gt;Spring&lt;/a&gt;, you can configure your beans explicitly using Java. I wasn't sure if the internal method calls would result in two instantiations of the dependency or not. Clearly, it works as expected, i.e. only one instance is created because the default mode for instantiating beans is singleton scope. The magic is called &lt;a target="_blank" href="http://cglib.sourceforge.net/"&gt;cglib&lt;/a&gt;.&lt;br /&gt;&lt;script type="syntaxhighlighter" class="brush: java;"&gt;&lt;![CDATA[&lt;br /&gt;public class ConfigTest {&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void shouldUseSingleInstanceEvenIfCreatedUsingMethodCall() {&lt;br /&gt;        ApplicationContext ctx = createContext(MethodConfig.class);&lt;br /&gt;        assertThat(ctx.getBean(Foo.class).dependency, sameInstance(ctx.getBean(Bar.class).dependency));&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void shouldUseSingleInstanceIfAutowired() {&lt;br /&gt;        ApplicationContext ctx = createContext(AutowiredConfig.class);&lt;br /&gt;        assertThat(ctx.getBean(Dependency.class), sameInstance(ctx.getBean(Baz.class).dependency));&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private ApplicationContext createContext(Class&lt;?&gt; config) {&lt;br /&gt;        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();&lt;br /&gt;        ctx.register(config);&lt;br /&gt;        ctx.refresh();&lt;br /&gt;        return ctx;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;]]&gt;&lt;/script&gt;&lt;br /&gt;&lt;script type="syntaxhighlighter" class="brush: java;"&gt;&lt;![CDATA[&lt;br /&gt;@Configuration&lt;br /&gt;public class MethodConfig {&lt;br /&gt;&lt;br /&gt;    @Bean&lt;br /&gt;    public Foo foo() {&lt;br /&gt;        return new Foo(dependency());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Bean&lt;br /&gt;    public Bar bar() {&lt;br /&gt;        return new Bar(dependency());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Bean&lt;br /&gt;    public Dependency dependency() {&lt;br /&gt;        return new Dependency();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;]]&gt;&lt;/script&gt;&lt;br /&gt;&lt;script type="syntaxhighlighter" class="brush: java"&gt;&lt;![CDATA[&lt;br /&gt;@Configuration&lt;br /&gt;@Import(MethodConfig.class)&lt;br /&gt;public class AutowiredConfig {&lt;br /&gt;&lt;br /&gt;    @Autowired&lt;br /&gt;    private Dependency dependency;&lt;br /&gt;&lt;br /&gt;    @Bean&lt;br /&gt;    public Baz baz() {&lt;br /&gt;        return new Baz(dependency);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;]]&gt;&lt;/script&gt;&lt;br /&gt;&lt;script type="syntaxhighlighter" class="brush: java;"&gt;&lt;![CDATA[&lt;br /&gt;public class Foo {&lt;br /&gt;    public final Dependency dependency;&lt;br /&gt;&lt;br /&gt;    public Foo(Dependency dependency) {&lt;br /&gt;        this.dependency = dependency;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;]]&gt;&lt;/script&gt;&lt;br /&gt;&lt;script type="syntaxhighlighter" class="brush: java;"&gt;&lt;![CDATA[&lt;br /&gt;public class Bar {&lt;br /&gt;    public final Dependency dependency;&lt;br /&gt;&lt;br /&gt;    public Bar(Dependency dependency) {&lt;br /&gt;        this.dependency = dependency;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;]]&gt;&lt;/script&gt;&lt;br /&gt;&lt;script type="syntaxhighlighter" class="brush: java;"&gt;&lt;![CDATA[&lt;br /&gt;public class Baz {&lt;br /&gt;    public final Dependency dependency;&lt;br /&gt;&lt;br /&gt;    public Baz(Dependency dependency) {&lt;br /&gt;        this.dependency = dependency;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;]]&gt;&lt;/script&gt;&lt;br /&gt;&lt;script type="syntaxhighlighter" class="brush: java;"&gt;&lt;![CDATA[&lt;br /&gt;public class Dependency {}&lt;br /&gt;]]&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-1185391616346242799?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/1185391616346242799/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=1185391616346242799" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/1185391616346242799?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/1185391616346242799?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/cd8O-u81wC0/using-spring-java-config-to-wire-up.html" title="Using Spring Java Config to wire up Dependencies" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://patforna.blogspot.com/2010/04/using-spring-java-config-to-wire-up.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0EHQXs5fip7ImA9WxFSEEw.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-1305158769707609297</id><published>2010-04-11T21:54:00.003+01:00</published><updated>2010-04-11T22:00:30.526+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-11T22:00:30.526+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><category scheme="http://www.blogger.com/atom/ns#" term="spring" /><category scheme="http://www.blogger.com/atom/ns#" term="javaconfig" /><title>Declarative Replacement of Annotation-Driven Bean Definitions in Spring</title><content type="html">If you are using &lt;a target="_blank" href="http://www.springsource.org/"&gt;Spring 3&lt;/a&gt; and configure your dependencies explicitly in a &lt;span style="font-family: courier new;font-size:85%;" &gt;@Configuration&lt;/span&gt; class, it's fairly easy to replace one dependency with another. All you need to do is declare two dependencies with the same bean name in two separate config classes. The beans of the config which is registered last to the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family: courier new;"&gt;ApplicationContext&lt;/span&gt;&lt;/span&gt;, will simply override the beans defined in previously registered config classes.&lt;br /&gt;&lt;br /&gt;Things get a bit more tricky, however, when you try to override a bean that has been wired up automatically using annotations. In this case, simply registering a config class that contains a bean with the same name as the annotated dependency doesn't yield the desired effect. It's very well possible that using a &lt;span style="font-size:85%;"&gt;&lt;span style="font-family: courier new;"&gt; JavaConfigApplicationContext&lt;/span&gt;&lt;/span&gt; might solve the issue but while &lt;a target="_blank" href="http://www.springsource.org/javaconfig"&gt;JavaConfig&lt;/a&gt; integration with the latest &lt;a target="_blank" href="http://www.springsource.org/"&gt;Spring 3&lt;/a&gt; &lt;a target="_blank" href="http://jira.springframework.org/browse/SJC-231"&gt;is flakey&lt;/a&gt;, this seems not an option. In the meantime, here's an example that demonstrates a solution to achieve the desired behaviour:&lt;br /&gt;&lt;script type="syntaxhighlighter" class="brush: java;"&gt;&lt;![CDATA[&lt;br /&gt;@Test&lt;br /&gt;public void shouldReplaceBean() {&lt;br /&gt;    AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();&lt;br /&gt;    parent.register(Square.class); // the annotated class&lt;br /&gt;    parent.refresh();&lt;br /&gt;&lt;br /&gt;    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();&lt;br /&gt;    ctx.setParent(parent); // bean definitions in child ctx will have precedence&lt;br /&gt;    ctx.register(ShapeConfig.class); // the config that will replace the Square bean&lt;br /&gt;    ctx.refresh();&lt;br /&gt;&lt;br /&gt;    assertThat(ctx.getBean(Shape.class), instanceOf(Circle.class));&lt;br /&gt;}&lt;br /&gt;]]&gt;&lt;/script&gt;&lt;br /&gt;&lt;script type="syntaxhighlighter" class="brush: java;"&gt;&lt;![CDATA[&lt;br /&gt;public interface Shape {}&lt;br /&gt;]]&gt;&lt;/script&gt;&lt;br /&gt;&lt;script type="syntaxhighlighter" class="brush: java;"&gt;&lt;![CDATA[&lt;br /&gt;@Component("shape") // an auto-detected 'shape' bean&lt;br /&gt;public class Square implements Shape {}&lt;br /&gt;]]&gt;&lt;/script&gt;&lt;br /&gt;&lt;script type="syntaxhighlighter" class="brush: java;"&gt;&lt;![CDATA[&lt;br /&gt;@Configuration&lt;br /&gt;public class ShapeConfig {&lt;br /&gt;    @Bean&lt;br /&gt;    public Shape shape() { // will replace the 'shape' bean&lt;br /&gt;        return new Circle();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;]]&gt;&lt;/script&gt;&lt;br /&gt;&lt;script type="syntaxhighlighter" class="brush: java;"&gt;&lt;![CDATA[&lt;br /&gt;public class Circle implements Shape {}&lt;br /&gt;]]&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-1305158769707609297?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/1305158769707609297/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=1305158769707609297" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/1305158769707609297?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/1305158769707609297?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/b2Yn-HlctzE/declarative-replacement-of-annotation.html" title="Declarative Replacement of Annotation-Driven Bean Definitions in Spring" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://patforna.blogspot.com/2010/04/declarative-replacement-of-annotation.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkQESX04eSp7ImA9WxFSEE0.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-2219408039010602664</id><published>2009-11-13T14:27:00.018Z</published><updated>2010-04-11T18:51:48.331+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-11T18:51:48.331+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><title>Lessons Learned (Part 2: Performance Testing and Garbage Collection)</title><content type="html">&lt;div style="text-align: justify;"&gt;This is a continuation of &lt;a target="_blank" href="http://patforna.blogspot.com/2009/10/lessons-learned-part-1-remembering.html"&gt;my previous blog post&lt;/a&gt;. The goal is to sum up some lessons that I've learned during the last couple of months while I was involved in performance tuning a large-scale distributed web-app.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:120%;" &gt;Golden Rules&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;Although there are probably many more rules and good advice out there, these are the ones that I remember off the top of my head as being important:&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Change one thing at a time&lt;/span&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;I often found myself very tempted to violate this rule. The problem with breaking it, however, is easily illustrated with an example: Imagine that you make two changes, &lt;span style="font-style: italic;"&gt;c1&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;c2&lt;/span&gt;, at the same time. If &lt;span style="font-style: italic;"&gt;c1&lt;/span&gt; results in a performance improvement of 20% and &lt;span style="font-style: italic;"&gt;c2&lt;/span&gt; in a performance penalty of 30% you'll get an overall performance deterioration of 10%. Consequently, you'll decide not to implement any of the changes, even though &lt;span style="font-style: italic;"&gt;c1&lt;/span&gt; on its own would have resulted in better performance.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Look at the system as a whole and fix the slowest running part&lt;/span&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;Even if you can make some part of the system thousands of times faster, it will not affect you application performance if the part you changed was not your primary bottleneck. For example, it doesn't make sense to optimise application code if the bottleneck is the result of a slow running database query. I'd even go as far as saying that it is harmful to optimise parts of the systems, when it's not needed. Firstly, it's a waste of time that could be used for tasks that provide more value. Secondly, making performance optimisations often introduces additional complexity at the code level. If you can't justify this extra complexity with a significant performance boost, don't do it. Of course, I'm not advocating against common sense and sound software design principles. For example, I know that making lots of fine-grained RPC calls is a bad idea, so I'll avoid it in the first place.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Don't optimise prematuerly, i.e. without measuring&lt;/span&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;This one probably goes hand in hand with the rule above. Don't optimise unless you can prove that it will have an effect on overall system performance. Again, this rule is not an excuse for not using sound software design principles.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:120%;" &gt;Performance Testing Cycle&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;Keeping the above rules in mind, we continuously iterated through the following cycle:&lt;br /&gt;&lt;/div&gt;&lt;ol style="text-align: justify;"&gt;&lt;li&gt;Measure performance&lt;/li&gt;&lt;li&gt;Identify single bottleneck (i.e. pick lowest hanging fruit)&lt;/li&gt;&lt;li&gt;Fix single bottleneck&lt;/li&gt;&lt;li&gt;Verify performance has improved&lt;/li&gt;&lt;/ol&gt;&lt;div style="text-align: justify;"&gt;Once step 4 is complete, the cycle restarts. Sometimes, we would loop through this cycle several times a day. Other times, one loop would take us several days or even weeks. This process essentially continued until our release target was reached.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:120%;" &gt;Measuring Performance&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;We used &lt;a target="_blank" href="http://jakarta.apache.org/jmeter/"&gt;JMeter&lt;/a&gt; to generate load against the application under test. We set up the tests so that the generated load would increase over time and therefore put the application increasingly under more stress. While running the tests, we measured a number of parameters. The most important ones were throughput, average response time and CPU utilisation.&lt;br /&gt;&lt;br /&gt;Looking at charts similar to the ones shown below, we got a fairly good understanding of how much load the application under test could handle.&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_MoEdKdmWnk0/Sv1uOpnLxpI/AAAAAAAAADk/qFckrFO2WI4/s1600-h/Picture+2.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 129px;" src="http://1.bp.blogspot.com/_MoEdKdmWnk0/Sv1uOpnLxpI/AAAAAAAAADk/qFckrFO2WI4/s400/Picture+2.png" alt="" id="BLOGGER_PHOTO_ID_5403596325881890450" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;In the above charts, for example, you can see that, at some point, application throughput reaches a plateau while the average response time per transactions continuous to grow. At this point, the application reached some physical or logical limit that prevented it from doing more work. The challenge, of course, is to find out what those constraints are in order to increase throughput or reduce response times.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:120%;" &gt;Identifying Bottlenecks&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;Bottlenecks created by hardware constraints are normally quite easy to identify. Usually, the symptoms are maximum CPU utilisation, reaching network bandwidth limits, etc. The solution is often to change and restructure application code. Identifying bottlenecks not directly created by hardware constraints is more difficult. Likely causes are slow running external systems, resource starvation, suboptimal configuration settings, etc.&lt;br /&gt;&lt;br /&gt;In the last project, we eliminated the hypothesis that slow running external systems are constraining our system quite early by taking them out of the equation completely and using stub implementations instead. At the same time, this made our performance tests much more robust, reliable and faster.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:120%;" &gt;Fixing Bottlenecks&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;In the first few weeks of our performance tuning initiative, we made quite a lot of progress. There were a large number of easily identifiable bottlenecks which were relatively trivial to fix. These included simple programming errors, unnecessary database calls, unnecessary network calls, slow running SQL queries, no caching where data was easily cacheable, concurrency issues, etc.&lt;br /&gt;&lt;br /&gt;After some time, however, it started to get more difficult to identify bottlenecks. In particular, there has been one case that I think is worth writing about.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Garbage Collection&lt;/span&gt;&lt;br /&gt;We had already spent several weeks trying to identify a bottleneck, which was not obviously caused by hardware constraints. Here are the things we noticed:&lt;br /&gt;&lt;/div&gt;&lt;ul style="text-align: justify;"&gt;&lt;li&gt;Throughput reached a plateau at point &lt;span style="font-style: italic;"&gt;t&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Response time grew significantly at the same point &lt;span style="font-style: italic;"&gt;t&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Hardware was far from being exhausted. CPU utilisation, for example, was about 60% at point &lt;span style="font-style: italic;"&gt;t&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Although total CPU utilisation was around 60%, one (of eight) cores was maxing out occasionally&lt;/li&gt;&lt;/ul&gt;&lt;div style="text-align: justify;"&gt;The last point was indicative that there was probably a CPU-intensive task executing in a single thread, hence single core. One such task that we could think of was garbage collection. We verified this using Perfmon and found that GC was indeed taking up a large amount of processing time (up to 30%).&lt;br /&gt;&lt;br /&gt;As a result, we did some reading on how .NET GC works. We've learned that, by default, the GC is optimised for standalone apps running on single-core machines (called Workstation GC). On multiprocessor machines, however, there is an additional GC mode available (called &lt;a target="_blank" href="http://msdn.microsoft.com/en-us/library/ms229357.aspx"&gt;Server GC&lt;/a&gt;). The difference between the two is basically that the latter creates a separate GC heap and GC thread for each processor and that collection occurs in parallel. Here's the change we made to our configuration:&lt;br /&gt;&lt;pre class="brush: xml; gutter: false;"&gt;&lt;br /&gt;&amp;lt;configuration&amp;gt;&lt;br /&gt;  &amp;lt;runtime&amp;gt;&lt;br /&gt;    &amp;lt;gcServer enabled="true" /&amp;gt;&lt;br /&gt;  &amp;lt;/runtime&amp;gt;&lt;br /&gt;&amp;lt;/configuration&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;After making the above configuration change, the throughput of our application increased by almost a factor 3! At the same time, we were again reaching 100% CPU utilisation and average GC time was down to 2-3%.&lt;br /&gt;&lt;br /&gt;Of course, this dramatic change meant that we were dealing with a completely new application profile. Consequently, we restarted our iterative cycle described above again from beginning in order to find the next bottleneck.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:120%;" &gt;Conclusion&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;The fundamental prerequisite for doing effective performance tuning is to have a set of repeatable and reliable performance tests. Ideally, these tests are easy to execute, finish in a reasonable amount of time and give you rapid feedback with regards to how the application is performing. Also, you'll need an isolated environment, which allows you to deploy new versions of the application easily and frequently. This gives you a good platform to experiment with changes. Measuring the difference between these changes with respect to the overall application performance then gives you the ability to make informed choices.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-2219408039010602664?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/2219408039010602664/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=2219408039010602664" title="12 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/2219408039010602664?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/2219408039010602664?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/hLFZr5vB0cM/lessons-learned-part-2-performance.html" title="Lessons Learned (Part 2: Performance Testing and Garbage Collection)" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_MoEdKdmWnk0/Sv1uOpnLxpI/AAAAAAAAADk/qFckrFO2WI4/s72-c/Picture+2.png" height="72" width="72" /><thr:total>12</thr:total><feedburner:origLink>http://patforna.blogspot.com/2009/11/lessons-learned-part-2-performance.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkIMQX88eCp7ImA9WxNbEUw.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-5423073339393887407</id><published>2009-11-13T11:05:00.002Z</published><updated>2009-11-13T11:09:40.170Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-13T11:09:40.170Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><title>RSpec and TextMate</title><content type="html">Just struggled to get the &lt;a href="http://rspec.info/" target="_blank"&gt;RSpec&lt;/a&gt; bundle working with &lt;a href="http://macromates.com/" target="_blank"&gt;TextMate&lt;/a&gt;. After installing the bundle as described on the &lt;a href="http://rspec.info/documentation/tools/extensions/editors/textmate.html" target="_blank"&gt;RSpec site&lt;/a&gt;, I kept getting the following error message:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;Library/Application Support/TextMate/Bundles/RSpec.tmbundle/Support/lib/spec/mate.rb:4:in `join': can't convert nil into String (TypeError)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;After a few hours of fruitless searching on the web, it occured to me that I should maybe have a look at the file listed in the error message above (&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;mate.rb:4&lt;/span&gt;&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;File.join(ENV['TM_PROJECT_DIRECTORY'],'vendor','plugins','rspec','lib')&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The bundle basically failed, because the &lt;span style=";font-family:courier new;font-size:85%;"&gt;TM_PROJECT_DIRECTORY&lt;/span&gt; variable was not set.&lt;br /&gt;&lt;br /&gt;So, instead of trying to run the file in standalone mode, I pulled the spec file into a new project (⌃⌘N) and ran it again.&lt;br /&gt;&lt;br /&gt;Et voilà. No errors this time round. ...and thanks so much for the helpful error message, RSpec-TextMate-bundle! *grrr*&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-5423073339393887407?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/5423073339393887407/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=5423073339393887407" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/5423073339393887407?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/5423073339393887407?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/0gieR7ChCvA/rspec-and-textmate.html" title="RSpec and TextMate" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>5</thr:total><feedburner:origLink>http://patforna.blogspot.com/2009/11/rspec-and-textmate.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkMMRX0zeCp7ImA9WxNVGEw.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-1679920337613614445</id><published>2009-10-29T11:01:00.001Z</published><updated>2009-10-29T12:14:44.380Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-29T12:14:44.380Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><title>Lessons Learned (Part 1: Remembering Waldo)</title><content type="html">&lt;span style="font-weight: bold;"&gt;Intro&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I've spent the last couple of months trying to help improve performance and scalability of a large web-based system. Initially, the application could barely handle more than a handful of concurrent users, which was far away from the launch target of several million users per day. This will probably be the first in a series of posts, in which I'd like to talk about some of the more interesting challenges we've faced.&lt;br /&gt;&lt;br /&gt;A bit of context first: The application was written in C#. The main technologies involved were WCF, MSMQ and SQLServer. Roughly speaking, the app consisted of a presentation tier (IIS/MVC), a business logic tier (WCF) and a data tier (SQLServer). Application logic was exposed through a large number of WCF service endpoints. Each service endpoint, in turn, exposed a similarly large number of fairly fine-grained service operations. Essentially, there were three groups of clients that consumed the exposed services: the presentation tier, other WCF services inside the business logic tier and a number of mobile clients (which I won't talk about here).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Intra-Tier Communication and Horizontal Scaling&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Inside the business logic tier, there was quite a lot of communication going on between the individual WCF services. Initially, many of these calls were routed through the WCF stack. The rationale behind this initial design decision was so that - if needed at a later stage - some services could be run individually on separate machines.&lt;br /&gt;&lt;br /&gt;It seemed unlikely, however, that this would ever happen. The logical conclusion of this thought was that services would communicate with each other via network calls, even though they would be running inside the same process. And even if the WCF service layer would be partitioned and distributed onto separate machines, what would happen if this still wouldn't give us the desired performance? Imagine you've got three WCF services: s1, s2 and s3. Assuming that s3 is the most hardware-hungry one, we could deploy s1 and s2 together on one machine and s3 separately on a dedicated machine. What happens, though, if the hardware onto which s3 is running is still insufficient? At this point, we could &lt;a href="http://news.zdnet.com/2422-13569_22-155070.html" target="_blank"&gt;start scaling the service out horizontally&lt;/a&gt; by adding a load balancer and more machines each running a copy of s3. So, if we probably need to scale out horizontally anyway at some stage, what's the point of adding the overhead and complexity of network calls between services if they can be run in same address space? To emphasise this point, we measured how many WCF service calls we can make in a given period (using net:TCP binding) and compared this against making direct in-memory calls to the same service instance. The not unexpected result: throughput for the latter was about 350 times higher. Consequently, we went through the codebase and replaced WCF service calls with normal method invocations wherever possible.&lt;br /&gt;&lt;br /&gt;In a recent email conversation, my colleague &lt;a href="http://www.martinfowler.com/" target="_blank"&gt;Martin Fowler&lt;/a&gt; drew an interesting analogy: &lt;span style="font-style: italic;"&gt;"It's interesting that there continues to be this desire to distribute different functionality onto different nodes in the name of scalability when often the better route is to put all nodes in the same process and cluster the resulting app. This was exactly the wrong thought that distributed objects suffered from."&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Inter-Tier Communication and Horizontal Scaling&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The presentation tier was physically separated from the WCF services running in the business logic tier. Consequently, communication between the two tiers had to happened over the network.&lt;br /&gt;&lt;br /&gt;Let's go back in time a little. Back in 1994, Waldo et al. wrote their excellent seminal paper called "&lt;a href="http://research.sun.com/techrep/1994/smli_tr-94-29.pdf" target="_blank"&gt;A Note on Distributed Computing&lt;/a&gt;". In it, they argue that there are fundamental differences between in-process and intra-process calls in terms of latency, concurrency, partial failures scenarios etc. In the past, RPC systems have tried to abstract these differences away and make developers believe that there's no difference between calling an object in the same memory space or executing a procedure on a remote machine.&lt;br /&gt;&lt;br /&gt;Don't get me wrong, I think that WCF is actually a pretty cool platform but, unfortunately, it also encourages people to continue building RPC apps in cases where other solutions might be more favorable. In fact, it makes it horribly easy to take a bunch of classes and expose them as remote objects. Calling them services doesn't mean that your application has now magically become service-oriented. Also, it doesn't change the fact that these now-called services are still remote objects including all the flaws that Waldo talked about.&lt;br /&gt;&lt;br /&gt;Indeed, we've had to fix a lot of code where developers happily looped over hundreds or thousands of items in order to retrieve some data, unaware that in each iteration they were making a network call. It goes without saying that this had a significant impact on the performance of the system. After removing all unnecessary calls, we measured the overhead incurred by network communication again. On average, still more than 30% of our total service execution time was network overhead (i.e. serialisation, WCF, TCP, network latency...).&lt;br /&gt;&lt;br /&gt;Looking at the chatty WCF service interfaces and the tight coupling with the code in the presentation tier it occurred to me that, in actual fact, we were not really building a distributed application but we were distributing an essentially monolithic application. Unfortunately, we didn't actually manage to change this one. I'm convinced, though, that a better way is to deploy two tightly coupled tiers together in the same process and then, again, &lt;a href="http://news.zdnet.com/2422-13569_22-155070.html" target="_blank"&gt;scale out horizontally&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-1679920337613614445?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/1679920337613614445/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=1679920337613614445" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/1679920337613614445?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/1679920337613614445?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/71NFOa_DL-k/lessons-learned-part-1-remembering.html" title="Lessons Learned (Part 1: Remembering Waldo)" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>5</thr:total><feedburner:origLink>http://patforna.blogspot.com/2009/10/lessons-learned-part-1-remembering.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUQMQHk-fCp7ImA9WxFSEE0.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-4324233959136011050</id><published>2009-10-12T12:50:00.004+01:00</published><updated>2010-04-11T18:36:21.754+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-11T18:36:21.754+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><title>Remove Unversioned Files in Subversion</title><content type="html">When using subversion, I more often than I'd like to admit find myself in need of starting with a clean working copy. Doing a normal (recursive) revert, however, does only undo changes from files that are already under version control and not newly added files which have to be removed separately.&lt;br /&gt;&lt;br /&gt;In order to remove all unversioned files from your working copy in one go, you can execute the following command. Think twice before using it, though ;)&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash; gutter: false;"&gt;svn st | grep "^\?" | sed "s/\? *//" | xargs rm -r&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-4324233959136011050?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/4324233959136011050/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=4324233959136011050" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/4324233959136011050?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/4324233959136011050?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/bSlBKsmluc8/remove-unversioned-files-in-subversion.html" title="Remove Unversioned Files in Subversion" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>3</thr:total><feedburner:origLink>http://patforna.blogspot.com/2009/10/remove-unversioned-files-in-subversion.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUANRno9eyp7ImA9WxFSEE0.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-316833591574933552</id><published>2009-03-10T23:12:00.006Z</published><updated>2010-04-11T18:43:17.463+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-11T18:43:17.463+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><category scheme="http://www.blogger.com/atom/ns#" term="web" /><title>JSONP Proxy Server</title><content type="html">I was doing some JSON mashing the other day and wanted to use data from different web pages. &lt;a href="http://en.wikipedia.org/wiki/XMLHttpRequest"&gt;XmlHttpRequest&lt;/a&gt; was not an option, because I wanted to &lt;a href="http://en.wikipedia.org/wiki/Cross-site_scripting"&gt;fetch content from different domains&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;As many web pages don't provide an interface that returns &lt;a href="http://bob.pythonmac.org/archives/2005/12/05/remote-json-jsonp/"&gt;JSONP&lt;/a&gt;, I wrote a very simple proxy server in python that does just that. More specifically, the proxy forwards the incoming request to a hard coded url and returns the result wrapped into a callback function which is provided by the user.&lt;script type="syntaxhighlighter" class="brush: python; gutter: false;"&gt;&lt;![CDATA[&lt;br /&gt;import urllib,re&lt;br /&gt;from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer&lt;br /&gt;&lt;br /&gt;class RequestHandler(BaseHTTPRequestHandler):&lt;br /&gt;&lt;br /&gt;    def do_GET(self):&lt;br /&gt;        query = re.search('.*(\?.*)', self.path).group(1)&lt;br /&gt;        callback = re.search('.*callback=([^&amp;amp;]*)', query).group(1)&lt;br /&gt;        queryWithoutCallback = re.sub('[&amp;amp;]?callback=[^&amp;amp;]*', '', query);&lt;br /&gt;        url = "http://funky.host.org/foo" + queryWithoutCallback;&lt;br /&gt;   &lt;br /&gt;        self.send_response(200)&lt;br /&gt;        self.send_header('Content-type', 'text/html')&lt;br /&gt;        self.end_headers()&lt;br /&gt; &lt;br /&gt;        f = urllib.urlopen(url)&lt;br /&gt;        response = f.read().strip()&lt;br /&gt;        f.close()&lt;br /&gt;&lt;br /&gt;        self.wfile.write("%s(%s);" % (callback, response))&lt;br /&gt;        return&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;    try:&lt;br /&gt;        server = HTTPServer(('', 80), RequestHandler)&lt;br /&gt;        print '--- Server ready!'&lt;br /&gt;        server.serve_forever()&lt;br /&gt;    except KeyboardInterrupt:&lt;br /&gt;        print '--- Shutting down...'&lt;br /&gt;        server.socket.close()&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    main()&lt;br /&gt;]]&gt;&lt;/script&gt;&lt;br /&gt;Once the proxy is running, you can hit it with &lt;a href="http://http//bob.pythonmac.org/archives/2005/12/05/remote-json-jsonp/"&gt;JSONP&lt;/a&gt; requests:&lt;script type="syntaxhighlighter" class="brush: javascript; gutter: false;"&gt;&lt;![CDATA[&lt;br /&gt;var script= document.createElement('script');&lt;br /&gt;script.type= 'text/javascript';&lt;br /&gt;script.src= 'http://localhost?foo=bar&amp;amp;callback=foobar';&lt;br /&gt;document.getElementsByTagName('head')[0].appendChild(script);&lt;br /&gt;&lt;br /&gt;function foobar(response) {&lt;br /&gt;    // do something funky with response&lt;br /&gt;}&lt;br /&gt;]]&gt;&lt;/script&gt;Or, if you prefer doing it in &lt;a href="http://jquery.com/"&gt;JQuery&lt;/a&gt;, which allows you to then handle the callback in a closure:&lt;br /&gt;&lt;script type="syntaxhighlighter" class="brush: javascript; gutter: false;"&gt;&lt;![CDATA[&lt;br /&gt;var url = 'http://localhost?foo=bar&amp;amp;callback=?';&lt;br /&gt;$.getJSON(url, function(response) {&lt;br /&gt;    // do something funky with response&lt;br /&gt;});&lt;br /&gt;]]&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-316833591574933552?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/316833591574933552/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=316833591574933552" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/316833591574933552?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/316833591574933552?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/E5HZDkihJg4/jsonp-proxy-server.html" title="JSONP Proxy Server" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>3</thr:total><feedburner:origLink>http://patforna.blogspot.com/2009/03/jsonp-proxy-server.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE4HSXY4eSp7ImA9WxRWGUs.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-5392722796537000668</id><published>2008-11-06T10:22:00.000Z</published><updated>2008-11-06T10:22:18.831Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-11-06T10:22:18.831Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><title>VMware and Windows Update</title><content type="html">For some reason, &lt;a href="http://www.windowsupdate.com"&gt;Windows Update&lt;/a&gt; doesn't seem to work out of the box in &lt;a href="http://www.vmware.com/"&gt;VMware&lt;/a&gt;. It took me a while to find &lt;a href="http://stateless.geek.nz/2008/06/01/windows-update-fails/"&gt;this&lt;/a&gt; so I thought I'll replicate the simple steps I had to take to get it working:&lt;br /&gt;&lt;ol&gt;&lt;li style="font-family: courier new;"&gt;net stop wuauserv&lt;/li&gt;&lt;li style="font-family: courier new;"&gt;regsvr32 %windir%\system32\wups2.dll&lt;/li&gt;&lt;li style="font-family: courier new;"&gt;net start wuauserv&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-5392722796537000668?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/5392722796537000668/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=5392722796537000668" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/5392722796537000668?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/5392722796537000668?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/s4_i2tGqh6E/vmware-and-windows-update.html" title="VMware and Windows Update" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://patforna.blogspot.com/2008/11/vmware-and-windows-update.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUADRHk4cSp7ImA9WxRXEEU.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-8640197372694253838</id><published>2008-10-15T16:11:00.004+01:00</published><updated>2008-10-15T16:16:15.739+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-10-15T16:16:15.739+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><title>Resizing a VMware Partition</title><content type="html">So, you would have thought that 8 GB of disk space for your virtual machine would be enough to do a bit of &lt;a href="http://www.microsoft.com/NET/"&gt;.NET&lt;/a&gt; development (i.e. install &lt;a href="http://www.microsoft.com/windows/windows-xp/"&gt;Windows XP&lt;/a&gt; and &lt;a href="http://msdn.microsoft.com/vstudio"&gt;Visual Studio 2008&lt;/a&gt;). Unfortunately, I was wrong and had to make my virtualised partition bigger. Making the virtual disk bigger is very straight forward. Resizing the actual (primary active) Windows partition, however, is a bit less trivial.&lt;br /&gt;&lt;br /&gt;Here's what I did:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Resize the &lt;a href="http://www.vmware.com/"&gt;VMware&lt;/a&gt; disk file;&lt;br /&gt;&lt;pre&gt;vmware-vdiskmanager -x 12GB /path/to/file.vmdk&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;Get knoppix iso (or other bootable Linux iso);&lt;/li&gt;&lt;li&gt;Use iso for your virtual machine's CD-ROM;&lt;/li&gt;&lt;li&gt;Boot your virtual machine (i.e. boot using knoppix); *&lt;/li&gt;&lt;li&gt;Use &lt;span style="font-family:courier new;"&gt;qtparted&lt;/span&gt; (or any other suitable tool on your chosen Linux distro) to resize the bursting partition.&lt;pre&gt;sudo qtparted&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;* I had trouble hitting ESC fast enough to get into the boot menu and therefore had to tell VMware to slow down a bit:&lt;br /&gt;&lt;pre&gt;echo 'bios.bootDelay="5000"' &gt;&gt; /path/to/file.vmx&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-8640197372694253838?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/8640197372694253838/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=8640197372694253838" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/8640197372694253838?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/8640197372694253838?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/q3pzIlWyi20/resizing-vmware-partition.html" title="Resizing a VMware Partition" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://patforna.blogspot.com/2008/10/resizing-vmware-partition.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkYHRn07fSp7ImA9WxFSEE0.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-5865438766836142869</id><published>2008-10-08T16:38:00.006+01:00</published><updated>2010-04-11T18:48:57.305+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-11T18:48:57.305+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><title>Ant EchoPath Task</title><content type="html">A few days ago, my pair and I have spent some time burning in &lt;a href="http://en.wikipedia.org/wiki/JAR_hell#JAR_hell"&gt;JAR Hell&lt;/a&gt;. As we tried to find out what was going on, we were reminded how cumbersome it is to get a classpath representation out of &lt;a href="http://ant.apache.org/"&gt;Ant&lt;/a&gt;. Fortunately, I don't have these kind of problems very often. Yet every time I do have them, I end up spending some time to figure out, how to get &lt;a href="http://ant.apache.org/"&gt;Ant&lt;/a&gt; to tell me what's on the classpath I'm using. Although there are a few blog posts that describe &lt;a href="http://blog.philhersh.com/weblog/2005/10/caffeine_induce.html"&gt;how to do that&lt;/a&gt; or even &lt;a href="http://peeps.dallas.focus-technologies.com/roller/page/lhankins?entry=more_readable_classpaths_for_ant"&gt;how to make the output actually readable&lt;/a&gt; I found it just too painful to go through the entire cycle from scratch each time.&lt;br /&gt;&lt;br /&gt;As a result, I hacked together an &lt;a href="http://ant.apache.org/"&gt;Ant&lt;/a&gt; task, which will tell you exactly what your path looks like.&lt;br /&gt;&lt;br /&gt;In your build file, just define a new &lt;span style="font-family:courier new;"&gt;echo-path&lt;/span&gt; task and then assign the path id you're puzzled about to the &lt;span style="font-family:courier new;"&gt;pathref&lt;/span&gt; attribute. For example:&lt;br /&gt;&lt;pre class="brush: xml; gutter: false;"&gt;&lt;br /&gt;&amp;lt;project name="echo-path" default="show-my-class-path"&amp;gt;&lt;br /&gt;&amp;lt;target name="show-my-class-path"&amp;gt;&lt;br /&gt;  &amp;lt;path id="my.class.path"&amp;gt;&lt;br /&gt;      &amp;lt;fileset dir="lib" includes="**/*.jar"/&amp;gt;&lt;br /&gt;  &amp;lt;/path&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;taskdef name="echo-path" classname="net.sf.orcus.EchoPathTask"&lt;br /&gt;           classpath="/home/patric/lib/echo-path-0.1.jar" /&amp;gt;&lt;br /&gt;  &amp;lt;echo-path pathref="my.class.path"/&amp;gt;&lt;br /&gt;&amp;lt;/target&amp;gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This will output something along the following lines:&lt;br /&gt;&lt;pre class="brush: plain; gutter: false;"&gt;&lt;br /&gt;show-my-class-path:&lt;br /&gt;[echo-path] Actual path for 'my.class.path':&lt;br /&gt;[echo-path] /home/patric/workspace/toy/lib/gwt-benchmark-viewer.jar&lt;br /&gt;[echo-path] /home/patric/workspace/toy/lib/gwt-dev-linux.jar&lt;br /&gt;[echo-path] /home/patric/workspace/toy/lib/gwt-servlet.jar&lt;br /&gt;[echo-path] /home/patric/workspace/toy/lib/gwt-user.jar&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The JAR containing the task can be &lt;a href="https://sourceforge.net/project/showfiles.php?group_id=139607&amp;amp;package_id=153101&amp;amp;release_id=631706"&gt;downloaded here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Update:&lt;/span&gt; In order to define the task, &lt;a href="https://sourceforge.net/project/showfiles.php?group_id=139607&amp;amp;package_id=153101&amp;amp;release_id=631706"&gt;echo-path-0.1.jar&lt;/a&gt; must be available. You can either place it anywhere on your file system and reference it using tha &lt;span style="font-family: courier new;"&gt;classpath&lt;/span&gt; attribute like in the example above. Alternatively, if you don't want to specify the JAR location each time, just put  &lt;a href="https://sourceforge.net/project/showfiles.php?group_id=139607&amp;amp;package_id=153101&amp;amp;release_id=631706"&gt;echo-path-0.1.jar&lt;/a&gt; in your &lt;span style="font-family:courier new;"&gt;ANT_HOME/lib&lt;/span&gt; directory&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-5865438766836142869?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/5865438766836142869/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=5865438766836142869" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/5865438766836142869?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/5865438766836142869?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/BR1S_hggDPQ/ant-echopath-task.html" title="Ant EchoPath Task" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>5</thr:total><feedburner:origLink>http://patforna.blogspot.com/2008/10/ant-echopath-task.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUIEQH06cCp7ImA9WxRTFUs.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-7296021383798111407</id><published>2008-09-04T23:12:00.005+01:00</published><updated>2008-09-04T23:31:41.318+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-04T23:31:41.318+01:00</app:edited><title>Two Remarkable Videos</title><content type="html">I know, I know, both of these videos have been circulating around the web for some time but I have only just now watched them in full length.&lt;br /&gt;&lt;br /&gt;I found them both - in their very own way - remarkable, deeply moving and extremely worthwhile watching:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.youtube.com/watch?v=ji5_MqicxSo"&gt;Last Lecture: Achieving Your Childhood Dreams&lt;/a&gt;&lt;br /&gt;Randy Pausch, Carnegie Mellon University&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.youtube.com/watch?v=TPAO-lZ4_hU"&gt;An Anthropological Introduction to YouTube&lt;/a&gt;&lt;br /&gt;Michael Welsch, Kansas State University&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-7296021383798111407?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/7296021383798111407/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=7296021383798111407" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/7296021383798111407?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/7296021383798111407?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/Mo71U8q7JSs/two-remarkable-videos.html" title="Two Remarkable Videos" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://patforna.blogspot.com/2008/09/two-remarkable-videos.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU8HQnw8eCp7ImA9WxdQFUg.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-8010897648135617455</id><published>2008-06-15T20:23:00.000+01:00</published><updated>2008-06-15T20:23:53.270+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-06-15T20:23:53.270+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="SOA" /><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><category scheme="http://www.blogger.com/atom/ns#" term="web services" /><title>Soya @ ThoughtWorks AwayDay</title><content type="html">Yesterday, I gave a talk on &lt;a target="_blank" href="http://soya.sourceforge.net/"&gt;Soya&lt;/a&gt; at &lt;a target="_blank" href="http://www.thoughtworks.com/"&gt;ThoughtWorks&lt;/a&gt; AwayDay. Some people have asked me if I could make the slides available and - even though they are basically just copies from previous talks - I have uploaded &lt;a target="_blank" href="http://soya.sourceforge.net/files/soya-awayday-2008.pdf"&gt;the slides here&lt;/a&gt;.&lt;a target="_blank" href="http://soya.sourceforge.net/files/soya-awayday-2008.pdf"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Also, you can download the &lt;a target="_blank" href="http://soya.sourceforge.net/files/thesis.pdf"&gt;original thesis here.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-8010897648135617455?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/8010897648135617455/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=8010897648135617455" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/8010897648135617455?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/8010897648135617455?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/bRKjDYnwNk8/soya-thoughtworks-awayday.html" title="Soya @ ThoughtWorks AwayDay" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://patforna.blogspot.com/2008/06/soya-thoughtworks-awayday.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE4BQnkzfip7ImA9WxdTGE4.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-2523909315995777386</id><published>2008-05-15T08:34:00.002+01:00</published><updated>2008-05-15T08:35:53.786+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-05-15T08:35:53.786+01:00</app:edited><title>alt.net</title><content type="html">There is an &lt;a target="_blank" href="http://upcoming.yahoo.com/event/495209/"&gt;alt.net gathering&lt;/a&gt; in a pub around the Clerkenwell area in London tonight. I am planning to go and see what it's like.&lt;br /&gt;&lt;br /&gt;There's no fixed agenda, everybody is welcome and it's happening at around 8:30pm in:&lt;br /&gt;&lt;br /&gt;&lt;a target="_blank" href="http://upcoming.yahoo.com/venue/138779/"&gt;The Crown Pub&lt;/a&gt;&lt;br /&gt;Sekforde Street&lt;br /&gt;London, EC1R&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-2523909315995777386?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/2523909315995777386/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=2523909315995777386" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/2523909315995777386?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/2523909315995777386?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/XbAGYonOpDA/altnet-l.html" title="alt.net" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://patforna.blogspot.com/2008/05/altnet-l.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE8GRnw6eSp7ImA9WxdTFE0.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-3005741345715337355</id><published>2008-05-06T22:25:00.002+01:00</published><updated>2008-05-10T09:07:07.211+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-05-10T09:07:07.211+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><title>Private Method Anti(?)-Pattern</title><content type="html">Some (actually quite a long) time ago, &lt;a target="_blank" href="http://monkeyisland.pl/"&gt;Szczepan&lt;/a&gt; asked if &lt;a target="_blank" href="http://monkeyisland.pl/2008/01/05/private-method-antipattern/"&gt;private methods are an anti-pattern&lt;/a&gt; and I've been promising him ever since to post a reply. The reasoning behind his statement was derived from the fact that TDD purists often maintain that untestable code is very likely bad code. Private methods are hard to test and it therefore seems to follow naturally that they, too, are a sign of bad code. While I'm sure that this is true in some cases, I think there are a lot of good reasons to have private methods or classes.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Implementation vs. Behavior&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Many developers focus too much on implementation details when they write tests. Ask yourself what you really care about a class: implementation details or behavior? It turns out that in most cases, one is only interested in the latter. So when writing tests, we should focus more on the public API of a class and not about its internal structure.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Modularization vs. Encapsulation&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In summary, I agree with &lt;a target="_blank" href="http://monkeyisland.pl/"&gt;Szczepan&lt;/a&gt; when he says that there is probably a lot of code out there in which private methods could be factored out into public classes or methods which would, in turn, increase code modularity. Often, however, encapsulation and a lean public API might be favored over modularization. More importantly, modularization might not always be needed, and therefore complicate and bloat the codebase unnecessarily. If modularization is still required at a later stage, internal code can be refactored and made public without breaking existing code.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Are You Over-Mocking?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Stubbing and mocking techniques and tools, especially &lt;a target="_blank" href="http://code.google.com/p/mockito/"&gt;Mockito&lt;/a&gt;, are great and make perfect sense in many situations (e.g. faking a network connection, a database, a service etc.). Breaking up a public method that consists of &lt;span style="font-style: italic;"&gt;n&lt;/span&gt; private methods into &lt;span style="font-style: italic;"&gt;n&lt;/span&gt; public methods of &lt;span style="font-style: italic;"&gt;n&lt;/span&gt; new classes only so that we can test that the methods are indeed invoked, however, introduces unwanted complexity and completely breaks encapsulation principles, without yielding any real benefits.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Don't Defend Yourself!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Private methods and classes are also a good thing because they allow to code non-defensively, which will make code simpler. As soon as logic is offered to the world as a public method, one needs to ensure that the implementation also caters for unexpected scenarios. If the implementation is private, on the other hand, it is easy to determine that a certain variable will never be &lt;span style="font-style: italic;"&gt;null&lt;/span&gt;, for example, and can thus be safely ignored.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Public APIs are Contracts&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The API of a class (i.e. it's non-private methods) represents a contract with whoever is going to use it. Sure, as long as the class is used only within a single source repository this might not be a big issues, because powerful refactoring tools make it easy to change the contracts. But what happens if code is being used externally - maybe because other people find it useful, too? It won't be possible to change public method signatures that easily anymore, because other code will have been built on top of them.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Reduce Public API complexity&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Private methods and classes can greatly reduce the complexity of a public API. Compare the Java API and its source code, for example. You'll realize that a lot of complexity is encapsulated in private methods and classes, which is a good thing, because it keeps the public APIs complexity low(er(-ish)).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;To sum up...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Although these are very much thoughts-in-progress, I think that in summary:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;We focus too much on implementation details and not enough on externally observable behavior of a class when writing tests&lt;/li&gt;&lt;li&gt;We often create code that is unnecessarily modular, because we are test-obsessed&lt;/li&gt;&lt;li&gt;We often mock too much (probably as a consequence or mutual reaction of the above two points)&lt;/li&gt;&lt;li&gt;We often create APIs that expose too much and therefore break encapsulation&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-3005741345715337355?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/3005741345715337355/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=3005741345715337355" title="6 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/3005741345715337355?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/3005741345715337355?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/-iYf83yCfRA/private-method-anti-pattern.html" title="Private Method Anti(?)-Pattern" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>6</thr:total><feedburner:origLink>http://patforna.blogspot.com/2008/05/private-method-anti-pattern.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkINRH07fCp7ImA9WxZVEU8.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-2150639474387265330</id><published>2008-03-21T17:25:00.004Z</published><updated>2008-03-21T17:29:55.304Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-03-21T17:29:55.304Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><title>Google Visualization API &amp; Gapminder</title><content type="html">Just checked out &lt;a href="http://code.google.com/apis/visualization/"&gt;Google's new visualization API&lt;/a&gt; and it seems that you can do pretty cool stuff with it. I really liked this one, for example:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.gapminder.org/world/"&gt;http://www.gapminder.org/world/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-2150639474387265330?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/2150639474387265330/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=2150639474387265330" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/2150639474387265330?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/2150639474387265330?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/DvPj7OwoK2Q/google-visualization-api-gapminder.html" title="Google Visualization API &amp; Gapminder" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://patforna.blogspot.com/2008/03/google-visualization-api-gapminder.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkQEQ34-fSp7ImA9WxZWFEg.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-8247689813804287077</id><published>2008-03-13T23:14:00.002Z</published><updated>2008-03-13T23:18:22.055Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-03-13T23:18:22.055Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><title>QCon, man boobs and random thoughts</title><content type="html">I just came back from &lt;a href="http://jaoo.dk/london-2008/conference/"&gt;QCon&lt;/a&gt; and my head still feels very fragmented with a number of random thoughts.&lt;br /&gt;&lt;br /&gt;&lt;a style="font-weight: bold;" href="http://jim.webber.name/"&gt;Jim&lt;/a&gt;&lt;span style="font-weight: bold;"&gt; and &lt;/span&gt;&lt;a style="font-weight: bold;" href="http://www.martinfowler.com/"&gt;Martin's&lt;/a&gt;&lt;span style="font-weight: bold;"&gt; keynote&lt;/span&gt; called '&lt;a href="http://jaoo.dk/london-2008/presentation/Does+my+Bus+look+big+in+this%3F"&gt;Does my Bus look big in this&lt;/a&gt;' mercilessly dissected current middleware approaches. It was and extraordinarily sharp-witted and entertaining talk in which they used their entire linguistic repertoire to pick on middleware vendors and conceive &lt;span style="font-style: italic;"&gt;beautifully eloquent metaphors&lt;/span&gt; such as "man boobs" (instead of bluntly calling it fat, ugly, bloated middleware products).&lt;br /&gt;&lt;br /&gt;&lt;a style="font-weight: bold;" href="http://www.threeriversinstitute.org/Kent%20Beck.htm"&gt;Kent Beck's&lt;/a&gt;&lt;span style="font-weight: bold;"&gt; &lt;/span&gt;&lt;a style="font-weight: bold;" href="http://jaoo.dk/london-2008/presentation/Keynote%3ATrends+in+Agile+Development"&gt;keynote&lt;/a&gt; was also quite interesting. He thinks that in the future we will write (even) more tests, deploy applications more frequently (apparently &lt;a href="http://www.flickr.com/"&gt;Flickr&lt;/a&gt; does it every 30 minutes!), work in teams that are more distributed, and solve more complex problems. He also believes that with the rise of a new generation of tech savvy business people, software developers will increasingly loose their 'wizard status' and need to invest more in their non-technical skills.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://jaoo.dk/london-2008/presentation/Blending+Java+with+dynamic+languages"&gt;Venkat Subramaniam&lt;/a&gt; and &lt;a href="http://jaoo.dk/london-2008/presentation/Evolving+the+JVM"&gt;Ola Bini&lt;/a&gt; both talked about using &lt;span style="font-weight: bold;"&gt;dynamic languages in Java and the JVM&lt;/span&gt;. Through &lt;a href="http://jcp.org/en/jsr/detail?id=223"&gt;JSR 223&lt;/a&gt;, Java 6 now includes support to run a range of scripting languages.  I was very pleased to see &lt;a href="http://jaoo.dk/london-2008/presentation/Blending+Java+with+dynamic+languages"&gt;Venkat&lt;/a&gt; demoing some &lt;a href="http://groovy.codehaus.org/"&gt;Groovy&lt;/a&gt; stuff that ran embedded in Java because we did the same with some &lt;a href="http://developer.mozilla.org/en/docs/JavaScript"&gt;JavaScript&lt;/a&gt; code on a client project just a few weeks ago.&lt;br /&gt;&lt;br /&gt;In his &lt;a href="http://jaoo.dk/london-2008/presentation/A+couple+of+ways+to+skin+an+Internet-scale+cat"&gt;second talk&lt;/a&gt;, &lt;a href="http://jim.webber.name/"&gt;Jim&lt;/a&gt; moaned a bit more about &lt;span style="font-weight: bold;"&gt;Web Services and the Web's architecture&lt;/span&gt;. He showed some pretty interesting ideas that use existing Web infrastructure (i.e. no WS-*) for integrating applications and realizing business workflows.&lt;br /&gt;&lt;br /&gt;Finally, I visited a &lt;span style="font-weight: bold;"&gt;birds of a feather session with &lt;/span&gt;&lt;a style="font-weight: bold;" href="http://blog.springframework.com/rod/"&gt;Rod Johnson&lt;/a&gt;. His appearance, however, did not necessarily remind me of a sunny, friendly day in &lt;a href="http://www.springframework.org/"&gt;Spring&lt;/a&gt;... ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-8247689813804287077?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/8247689813804287077/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=8247689813804287077" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/8247689813804287077?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/8247689813804287077?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/5xA2kHg2BUo/qcon-man-boobs-and-random-thoughts.html" title="QCon, man boobs and random thoughts" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://patforna.blogspot.com/2008/03/qcon-man-boobs-and-random-thoughts.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0IMSHk5cSp7ImA9WxZXFkw.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-3247766777654271094</id><published>2008-03-03T21:33:00.000Z</published><updated>2008-03-04T08:33:09.729Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-03-04T08:33:09.729Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="SOA" /><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><category scheme="http://www.blogger.com/atom/ns#" term="web services" /><category scheme="http://www.blogger.com/atom/ns#" term="web" /><title>QCon 2008</title><content type="html">Just got a one-day conference ticket for this year's &lt;a href="http://qcon.infoq.com/london/conference/"&gt;QCon&lt;/a&gt; in London. I'll probably be attending most of the  &lt;a href="http://qcon.infoq.com/london/tracks/show_track.jsp?trackOID=87"&gt;SOA, REST and the Web&lt;/a&gt; sessions on Thursday, March 13.&lt;br /&gt;&lt;br /&gt;Really looking forward to going =)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-3247766777654271094?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/3247766777654271094/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=3247766777654271094" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/3247766777654271094?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/3247766777654271094?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/2NENyCLE-PE/qcon-2008.html" title="QCon 2008" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://patforna.blogspot.com/2008/03/qcon-2008.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU8GRHoyeSp7ImA9WxJQEUs.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-7025491413108708881</id><published>2008-02-25T11:07:00.003Z</published><updated>2009-05-24T13:23:45.491+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-24T13:23:45.491+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><title>Installing Ubuntu from Memory Stick</title><content type="html">If you - like me - have an ultra-portable notebook (marketing slang for 'comes with no CD-ROM') and no external CD-ROM (why should you?), you might know that it can sometimes be a bit tricky to install an OS from scratch.&lt;br /&gt;&lt;br /&gt;I was thus all the more positively surprised by how easy it was to install Ubuntu from a USB memory stick onto my machine. Here's basically what I did*:&lt;br /&gt;&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;"  &gt;*(this assumes: virtual CD-ROM =&gt; F:&lt;/span&gt;&lt;span style="font-size:85%;"&gt;, USB stick =&gt; &lt;/span&gt;&lt;span style=";font-family:courier new;font-size:85%;"  &gt;G:&lt;/span&gt;&lt;span style="font-size:85%;"&gt;, syslinux =&gt; &lt;/span&gt;&lt;span style=";font-family:courier new;font-size:85%;"  &gt;c:\syslinux&lt;/span&gt;&lt;span style="font-size:85%;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Download &lt;a href="http://www.ubuntu.com/getubuntu/download"&gt;Ubuntu ISO image&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Download and install &lt;a href="http://syslinux.zytor.com/"&gt;Syslinux&lt;/a&gt; and &lt;a href="http://www.daemon-tools.cc/"&gt;Daemon Tools&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Plug in USB stick and make sure you have no more &lt;a href="http://en.wikipedia.org/wiki/U3"&gt;U3&lt;/a&gt; stuff installed on your stick.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Format the stick:&lt;br /&gt;&lt;span style="margin-left: 20px;font-family:courier new;font-size:85%;"  &gt;format g: /FS:FAT /Q /X&lt;/span&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Make stick bootable:&lt;br /&gt;&lt;span style="margin-left: 20px;font-family:courier new;font-size:85%;"  &gt;c:\syslinux\win32\syslinux -s G:&lt;/span&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Mount Ubuntu ISO image to drive &lt;span style=";font-family:courier new;font-size:85%;"  &gt;F:&lt;/span&gt; (e.g. using &lt;a href="http://www.daemon-tools.cc/"&gt;Daemon Tools&lt;/a&gt;)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Copy some files from virtual CD-ROM to stick:&lt;br /&gt;&lt;span style="margin-left: 20px;font-family:courier new;font-size:85%;"  &gt;xcopy /e /h /k f:\*.* g:&lt;/span&gt;&lt;br /&gt;&lt;span style="margin-left: 20px;font-family:courier new;font-size:85%;"  &gt;xcopy /e /h /k g:\install\*.* g:&lt;/span&gt;&lt;br /&gt;&lt;span style="margin-left: 20px;font-family:courier new;font-size:85%;"  &gt;xcopy /e /h /k g:\isolinux\*.* g:&lt;/span&gt;&lt;br /&gt;&lt;span style="margin-left: 20px;font-family:courier new;font-size:85%;"  &gt;copy g:\casper\vmlinuz g:&lt;/span&gt;&lt;br /&gt;&lt;span style="margin-left: 20px;font-family:courier new;font-size:85%;"  &gt;copy g:\casper\initrd.gz g:&lt;/span&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Rename a config file:&lt;br /&gt;&lt;span style="margin-left: 20px;font-family:courier new;font-size:85%;"  &gt;move g:\isolinux.cfg g:\syslinux.cfg&lt;/span&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Edit &lt;span style=";font-family:courier new;font-size:85%;"  &gt;syslinux.cfg&lt;/span&gt; by removing all &lt;span style=";font-family:courier new;font-size:85%;"  &gt;/casper&lt;/span&gt; and &lt;span style=";font-family:courier new;font-size:85%;"  &gt;/install&lt;/span&gt; references. (Not necessary for Ubuntu 9.x)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;That's it. Adjust your BIOS boot settings and boot from the stick.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-7025491413108708881?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/7025491413108708881/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=7025491413108708881" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/7025491413108708881?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/7025491413108708881?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/HpROMX-e8X8/installing-ubuntu-from-memory-stick.html" title="Installing Ubuntu from Memory Stick" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://patforna.blogspot.com/2008/02/installing-ubuntu-from-memory-stick.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkMNRH07cCp7ImA9WxZQFEw.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-5950570418319805094</id><published>2008-01-15T12:06:00.002Z</published><updated>2008-02-19T08:41:35.308Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-02-19T08:41:35.308Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><title>Mockito</title><content type="html">My colleague &lt;a href="http://monkeyisland.pl/" target="_blank="&gt;Szczepan&lt;/a&gt; has just released a new mocking library called &lt;a href="http://code.google.com/p/mockito/" target="_blank"&gt;Mockito&lt;/a&gt; (I leave it up to your imagination to picture the situation in which he came up with the name...).&lt;br /&gt;&lt;br /&gt;Essentially, &lt;a href="http://code.google.com/p/mockito/" target="_blank"&gt;Mockito&lt;/a&gt; is built upon &lt;a href="http://easymock.org/" target="_blank"&gt;EasyMock&lt;/a&gt; but seems to provide a more elegant approach and simpler API to testing using mock objects than the latter. For example, I always found the &lt;a href="http://easymock.org/" target="_blank"&gt;EasyMock's&lt;/a&gt; recording and replaying stages quite cumbersome. Not suprisingly, I'm thus really glad to see that &lt;a href="http://monkeyisland.pl/" target="_blank="&gt;Szczepan&lt;/a&gt; came up with a simpler solution in which stubbing and verification are separated into different steps. This means that you stub your objects first, then use them, and finally verify only the interactions (with the mock objects) that you're interested in.&lt;br /&gt;&lt;br /&gt;The following sample code is from the &lt;a href="http://code.google.com/p/mockito/" target="_blank"&gt;Mockito&lt;/a&gt; project site:&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;"  &gt;import static org.mockito.Mockito.*;&lt;br /&gt;&lt;br /&gt;//let's mock a List!&lt;br /&gt;List mockedList = mock(List.class);&lt;br /&gt;&lt;br /&gt;//stubbing&lt;br /&gt;stub(mockedList.get(0)).toReturn("first element");&lt;br /&gt;&lt;br /&gt;//using mock object&lt;br /&gt;mockedList.add("one");&lt;br /&gt;mockedList.clear();&lt;br /&gt;&lt;br /&gt;//verification&lt;br /&gt;verify(mockedList).add("one");&lt;br /&gt;verify(mockedList).clear();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I really look forward to using it more and finding out if it indeed makes testing easier. It surely does look promising =)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-5950570418319805094?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/5950570418319805094/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=5950570418319805094" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/5950570418319805094?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/5950570418319805094?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/iInnW7XQCQI/mockito.html" title="Mockito" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://patforna.blogspot.com/2008/01/mockito.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0MDSXk4eyp7ImA9WB9UEEo.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-1972762690821036349</id><published>2007-12-08T12:45:00.000Z</published><updated>2007-12-07T23:51:18.733Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-12-07T23:51:18.733Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><title>Implementation Patterns</title><content type="html">A couple of days ago I read Kent Beck's fairly recent book called &lt;a target="_blank" href="http://www.amazon.co.uk/Implementation-Patterns-Addison-Wesley-Signature-Kent/dp/0321413091/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1197060598&amp;amp;sr=8-1"&gt;Implementation Patterns&lt;/a&gt;. I especially enjoyed the more philosophical chapters at the beginning. In this part of the book, Beck explains why he thinks it's important that developers should try really hard to write &lt;span style="font-style: italic;"&gt;good&lt;/span&gt; code.&lt;br /&gt;&lt;br /&gt;In terms of books relating to software engineering, I normally prefer slim books over weighty tomes but found this one - which belongs to the former category - by and large a bit too laconic. In fact, one could probably argue that it is not a lot more than merely a very good introduction to programming in Java.&lt;br /&gt;&lt;br /&gt;Nevertheless, I had a good time reading it and here are the key points that I'm taking with me:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Much more time is spent reading code than writing it.&lt;/li&gt;&lt;li&gt;Code that's easy to understand is more maintainable.&lt;/li&gt;&lt;li&gt;Try extremely hard to come up with concise and meaningful names for classes, methods and variables.&lt;/li&gt;&lt;li&gt;First and foremost, optimise code for readability not performance.&lt;/li&gt;&lt;li&gt;Don't restrict yourself to a specific case if you can easily be more generic (e.g. use &lt;span style=";font-family:courier new;font-size:85%;"  &gt;Collection&lt;/span&gt; instead of &lt;span style=";font-family:courier new;font-size:85%;"  &gt;List&lt;/span&gt; if your code doesn't require index-based element access).&lt;/li&gt;&lt;li&gt;Don't make members more visible than they absolutely need to.&lt;/li&gt;&lt;li&gt;Keep data and logic as close together as possible (i.e. encapsulate).&lt;/li&gt;&lt;li&gt;Don't flinch from creating one-line methods if they increase readability (e.g. write &lt;span style=";font-family:courier new;font-size:85%;"  &gt;void setLoadedFlat();&lt;/span&gt; instead of &lt;span style=";font-family:courier new;font-size:85%;"  &gt;loaded |= LOADED_BIT;&lt;/span&gt;).&lt;/li&gt;&lt;li&gt;[Probably my favourite part in the book:] Don't &lt;span style="font-style: italic;"&gt;"thoughtlessly obey programming folklore"&lt;/span&gt; (e.g. don't always stubbornly use only one return statement if you can use guard statements such as:&lt;pre style="font-family: courier new; font-size: 85%; padding-left: 20px;"&gt;void compute() {&lt;br /&gt;  Client client = getClient();&lt;br /&gt;  if (client == null)&lt;br /&gt;    return;&lt;br /&gt;  Request request = client.getRequest();&lt;br /&gt;  if (client.getRequest == null)&lt;br /&gt;    return;&lt;br /&gt;  process(request);&lt;br /&gt;}&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;Implementation strategy is the extraneous information most often included in method names (e.g. use &lt;span style=";font-family:courier new;font-size:85%;"  &gt;Poll.vote()&lt;/span&gt; instead of &lt;span style=";font-family:courier new;font-size:85%;"  &gt;Poll.incrementCounter()&lt;/span&gt;).&lt;/li&gt;&lt;li&gt;Express as much information as possible through code and structure rather than comments.&lt;/li&gt;&lt;/ul&gt; Although most of these points seem trivial, I actually catch myself continuously violating one or the other. The book encouraged me question why I'm doing certain things one way and not the other. This, in turn, forces me to either come up with solid reasoning supporting my view or change my habits.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-1972762690821036349?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/1972762690821036349/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=1972762690821036349" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/1972762690821036349?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/1972762690821036349?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/4tQeS3FaHbk/implementation-patterns.html" title="Implementation Patterns" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://patforna.blogspot.com/2007/12/implementation-patterns.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0cGRXc6cCp7ImA9WB9VE0U.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-3474323346249198650</id><published>2007-11-29T12:59:00.000Z</published><updated>2007-11-30T00:03:44.918Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-11-30T00:03:44.918Z</app:edited><title>At last...</title><content type="html">Today I have at last completed the last bit of my thesis submission: I have posted a hard copy of my final thesis version to the &lt;a href="http://www.grs.unsw.edu.au/" target="_blank"&gt;Graduate Research School at UNSW&lt;/a&gt; and also submitted it electronically to &lt;a href="http://adt.caul.edu.au/" target="_blank"&gt;ADT&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;If you're interested, you can download the full thesis from the &lt;a href="http://soya.sourceforge.net/publications.html" target="_blank"&gt;Soya web site&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;A big thank you again to everybody for bearing with me and supporting me throughout the last 18 (or so) months =)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-3474323346249198650?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/3474323346249198650/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=3474323346249198650" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/3474323346249198650?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/3474323346249198650?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/Zo4GmrXDOpE/at-last.html" title="At last..." /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://patforna.blogspot.com/2007/11/at-last.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEEBRH09eip7ImA9WB9WFkU.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-3627917911027049906</id><published>2007-11-21T23:02:00.000Z</published><updated>2007-11-21T23:10:55.362Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-11-21T23:10:55.362Z</app:edited><title>Ability and Responsibility</title><content type="html">The following three events triggered some (probably still incomplete) thoughts on my behalf:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;A few weeks ago I had a discussion with a friend about whether I would write software for military projects or the like. I told him unmistakably that I wouldn't engage in such work, even if it would be at the cost of giving up my job.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Last week then, I read Kent Beck's new book called &lt;a href="http://www.infoq.com/articles/implementations-patterns-br" target="_blank"&gt;Implementation Patterns&lt;/a&gt;. While reading it, a passage caught my attention in which he - in one breath - associates coding with responsibility.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Today finally, I read &lt;a href="http://blog4aurobindo.blogspot.com/2007/11/toward-discussion-of-morality-and-code.html" target="_blank"&gt;a blog post&lt;/a&gt; which partially records a talk given by &lt;a href="http://www.thoughtworks.com/" target="_blank"&gt;ThoughtWorks&lt;/a&gt; founder Roy Singham. In a similar way to the above, Roy defines his position relating to coding, morality and military projects.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;A number of other people in this world including me have been very lucky, because we were allowed to grow up in a safe and developed environment. We have been fortunate enough to go to university and learn about many things from previous generations. We have been given lots of time, support, options and opportunities. Moreover, it seems like we've also been given the choice to decide what to do with all these gifts.&lt;br /&gt;&lt;br /&gt;So, what do we do with all of them? I believe that we can indeed leverage them to do really, really good things, such as making people's lives better who haven't been as lucky as we've been. At the same time, we could likewise misuse those gifts to do an awful lot of damage. Think about it: surely it wasn't somebody who didn't know how to read who invented the most sophisticated and deadly weapons of mass destruction; surely it wasn't somebody who didn't know anything about higher mathematics who designed and created the latest ultra precise killing high-tech military weaponry. I'm sure you can think of many more examples.&lt;br /&gt;&lt;br /&gt;What I'm trying to say is that many of us have been given extraordinary gifts that can be used to achieve extraordinary things. In both ways, good or bad. The higher our abilities, the bigger the effect we can have on our environment. I think that based on solid moral grounds using these gifts in a good way is in fact not a choice, it's a responsibility.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-3627917911027049906?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/3627917911027049906/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=3627917911027049906" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/3627917911027049906?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/3627917911027049906?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/O-aK8xph7RA/ability-and-responsibility.html" title="Ability and Responsibility" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://patforna.blogspot.com/2007/11/ability-and-responsibility.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUUERHo9fip7ImA9WB9WFUw.&quot;"><id>tag:blogger.com,1999:blog-34378650.post-3092053581875602531</id><published>2007-11-19T22:49:00.000Z</published><updated>2007-11-19T23:00:05.466Z</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-11-19T23:00:05.466Z</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="software development" /><title>Finding Duplicate Table Entries</title><content type="html">The following piece of SQL is pretty handy for finding duplicate entries in a table. It will return all rows that have a single or aggregate column value which exist more than once in the given table.&lt;br /&gt;&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;"&gt;select column_x, column_y, column_z&lt;br /&gt;from my_table&lt;br /&gt;group by column_x, column_y, column_z&lt;br /&gt;having count (*) &gt; 1;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/34378650-3092053581875602531?l=patforna.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://patforna.blogspot.com/feeds/3092053581875602531/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=34378650&amp;postID=3092053581875602531" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/3092053581875602531?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/34378650/posts/default/3092053581875602531?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patforna/~3/dOIa-QS5LHg/finding-duplicate-table-entries.html" title="Finding Duplicate Table Entries" /><author><name>Patric Fornasier</name><uri>http://www.blogger.com/profile/06567767149588932272</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/--d184PFrFUA/TZnUlPUQ4cI/AAAAAAAAAIw/oCytt2gP1h8/s220/me.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://patforna.blogspot.com/2007/11/finding-duplicate-table-entries.html</feedburner:origLink></entry></feed>

