<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-9139942395882504841</id><updated>2011-10-31T07:57:28.429+01:00</updated><category term='Use cases'/><category term='JPA'/><category term='Robustness analysis'/><category term='reverse engineer'/><category term='Hibernate'/><category term='HiberObjects'/><category term='communication'/><category term='SQLite'/><category term='Java'/><category term='complexity'/><category term='Groovy'/><category term='Mercurial'/><category term='JDBC'/><category term='Swing'/><category term='TDD'/><category term='agile'/><category term='SmartGWT'/><category term='object cases'/><category term='Eclipse'/><category term='DB Importer'/><category term='unit testing'/><category term='testing'/><category term='requirements'/><category term='examples'/><category term='database'/><title type='text'>The Pessimistic Programmer</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>29</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-3421642571379817508</id><published>2011-06-21T17:48:00.003+02:00</published><updated>2011-06-21T19:15:42.006+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unit testing'/><category scheme='http://www.blogger.com/atom/ns#' term='object cases'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><category scheme='http://www.blogger.com/atom/ns#' term='SmartGWT'/><title type='text'>Test driven development with SmartGWT and JPA</title><content type='html'>SmartGWT is a collection of GWT widgets that can easily be connected to database tables or some other data on the server.&lt;br /&gt;&lt;br /&gt;SmartGWT Pro supports Hibernate/JPA on the server, but there are some limitations, and unit testing it is tricky.&lt;br /&gt;&lt;br /&gt;This article shows how to implement and test server logic with SmartGWT and JPA. Specifically, it will show how to:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Configure server.properties for unit testing.&lt;/li&gt;&lt;li&gt;Handle JPA relations with SmartGWT.&lt;/li&gt;&lt;li&gt;Implement an EMF provider so the unit tests and the data sources share the same EntityManager.&lt;/li&gt;&lt;li&gt;Use special JPA queries such as &amp;lt;= and string matching in SmartGWT.&lt;/li&gt;&lt;li&gt;Build complete server logic with pagination and sorting for JPA.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h2&gt;Examples&lt;/h2&gt;Before we get into the practicalities of SmartGWT, we need to design good tests. Many who start with test driven development find this very hard. My approach is to test examples of application behavior. If you discuss these examples with the customer first, you can implement automatic tests that are directly based on the requirements.&lt;br /&gt;&lt;br /&gt;Let's say I wanted to build an online auction application. I would start by asking the client for some examples of what the application should do. The client might answer that sellers may place items for auction, bidders place bids, and the seller then sells to the highest bidder. This is not what I mean with examples, these are abstract requirements. I want concrete examples with realistic input and output values. I mean something like this:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: white; border-style: solid; border-width: 1px; color: black; padding-left: 1em; padding-top: 1em;"&gt;Jane wants to sell an electric guitar. She enters the following information:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-mr-OE3xOpDo/TgCtP0pWTHI/AAAAAAAAAF8/KvjS8ghT6-E/s1600/Create%2Bauction.png" imageanchor="1"&gt;&lt;img border="0" height="146" src="http://4.bp.blogspot.com/-mr-OE3xOpDo/TgCtP0pWTHI/AAAAAAAAAF8/KvjS8ghT6-E/s400/Create%2Bauction.png" width="228" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="background-color: white; border-style: solid; border-width: 1px; color: black; padding-left: 1em; padding-top: 1em;"&gt;Bob wants to buy an electric guitar. He searches for auctions, clicks on Electric guitar, and places a bid.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-eZcaKYy5_xc/TgCuTIGSREI/AAAAAAAAAGE/kzY8_eQL1fk/s1600/Search%2Bfor%2Bauctions.png" imageanchor="1"&gt;&lt;img border="0" height="217" src="http://1.bp.blogspot.com/-eZcaKYy5_xc/TgCuTIGSREI/AAAAAAAAAGE/kzY8_eQL1fk/s400/Search%2Bfor%2Bauctions.png" width="206" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-wEgK0BvgWDk/TgCuTEVTCdI/AAAAAAAAAGM/PLkX6W4VUo0/s1600/Auction%2Bdetails.png" imageanchor="1"&gt;&lt;img border="0" height="147" src="http://2.bp.blogspot.com/-wEgK0BvgWDk/TgCuTEVTCdI/AAAAAAAAAGM/PLkX6W4VUo0/s400/Auction%2Bdetails.png" width="228" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-eoD-tosfCg0/TgCuTWiPr7I/AAAAAAAAAGU/iqKLYC9x4qk/s1600/Place%2Bbid.png" imageanchor="1"&gt;&lt;img border="0" height="97" src="http://3.bp.blogspot.com/-eoD-tosfCg0/TgCuTWiPr7I/AAAAAAAAAGU/iqKLYC9x4qk/s400/Place%2Bbid.png" width="176" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="background-color: white; border-style: solid; border-width: 1px; color: black; padding-left: 1em; padding-top: 1em;"&gt;Jane can then see the bids in her auction:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-RzyvPW5AKzU/TgCvFI-FZNI/AAAAAAAAAGc/k016hU2kUu8/s1600/Auction%2Bdetails%2Bwith%2Bbid.png" imageanchor="1"&gt;&lt;img border="0" height="145" src="http://4.bp.blogspot.com/-RzyvPW5AKzU/TgCvFI-FZNI/AAAAAAAAAGc/k016hU2kUu8/s400/Auction%2Bdetails%2Bwith%2Bbid.png" width="231" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="bold"&gt;The advantages of using examples are that they are detailed, they reveal a lot of misunderstandings and they can be tested.&lt;/div&gt;&lt;br /&gt;&lt;h2&gt;Objects&lt;/h2&gt;Another thing that many struggle with is to design a good domain model, be it a class model or a database schema. The approach shown here makes this easy. Instead of starting with a class diagram, I start by looking for objects in the examples. Then I derive the class model from the objects.&lt;br /&gt;&lt;br /&gt;An example connected to objects is called an &lt;i&gt;object case&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Jane wants to sell an electric guitar&lt;/h3&gt;For each example, I need to determine how to handle the input from the user and how to calculate the output. In this example, I will create a User object and an Auction object. The arrows show that all the input from the user is stored in these objects.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-9Hc271yO3r4/TgCwFCQHYtI/AAAAAAAAAGk/-5cm47C0DDE/s1600/Create%2Bauction%2Bobjects.png" imageanchor="1"&gt;&lt;img border="0" height="148" src="http://2.bp.blogspot.com/-9Hc271yO3r4/TgCwFCQHYtI/AAAAAAAAAGk/-5cm47C0DDE/s1600/Create%2Bauction%2Bobjects.png" width="500" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;Bob searches for auctions&lt;/h3&gt;In this example, the Free_text and Max_price input is used to search for Auctions, but the Category is not used. This means that something is missing in the object model.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-NPPA0sLFCyU/TgCwvGKML4I/AAAAAAAAAGs/AnQ0IrX4K6I/s1600/Search%2Bfor%2Bauctions%2Bobjects%2B1.png" imageanchor="1"&gt;&lt;img border="0" height="164" src="http://3.bp.blogspot.com/-NPPA0sLFCyU/TgCwvGKML4I/AAAAAAAAAGs/AnQ0IrX4K6I/s1600/Search%2Bfor%2Bauctions%2Bobjects%2B1.png" width="600" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;I decide to introduce a new object that will be used to search for category. Now, all input values are used and the output in the table is calculated from the Auction objects, so everything is covered.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-A_EuVma88c8/TgCxuzE5SmI/AAAAAAAAAG0/vtlvGLYiGnk/s1600/Search%2Bfor%2Bauctions%2Bobjects%2B2.png" imageanchor="1"&gt;&lt;img border="0" height="164" src="http://3.bp.blogspot.com/-A_EuVma88c8/TgCxuzE5SmI/AAAAAAAAAG0/vtlvGLYiGnk/s1600/Search%2Bfor%2Bauctions%2Bobjects%2B2.png" width="600" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;Bob clicks on Electric guitar&lt;/h3&gt;All the output values can be calculated from an Auction object. We should define another example that shows an actual bid.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-uS1slCAqWsM/TgCyBiAWovI/AAAAAAAAAG8/64DE-FpHzjI/s1600/Auction%2Bdetails%2Bobjects.png" imageanchor="1"&gt;&lt;img border="0" height="136" src="http://3.bp.blogspot.com/-uS1slCAqWsM/TgCyBiAWovI/AAAAAAAAAG8/64DE-FpHzjI/s1600/Auction%2Bdetails%2Bobjects.png" width="600" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;Bob places a bid&lt;/h3&gt;The input values are stored in two new objects: User and Bid. What happens if the user already exists? We should add another example to deal with this.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-K5yRmbXiqaY/TgCyR6s9CeI/AAAAAAAAAHE/OvEUreirbb4/s1600/Place%2Bbid%2Bobjects.png" imageanchor="1"&gt;&lt;img border="0" height="190" src="http://3.bp.blogspot.com/-K5yRmbXiqaY/TgCyR6s9CeI/AAAAAAAAAHE/OvEUreirbb4/s1600/Place%2Bbid%2Bobjects.png" width="600" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;Classes&lt;/h3&gt;All input and output values are now connected to objects. This is a simple and powerful method to design an object model. Based on these objects, I can create the following class diagram:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-FJR8U4rhdHk/TgCyjIVJYmI/AAAAAAAAAHM/yYf54bd8Duc/s1600/auctions-classes.gif" imageanchor="1"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-FJR8U4rhdHk/TgCyjIVJYmI/AAAAAAAAAHM/yYf54bd8Duc/s1600/auctions-classes.gif" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;Find more functionality&lt;/h3&gt;We can use the class diagram to find missing functionality. The diagram below shows how objects are created and read by the examples defined so far.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-kOEhquI1WcY/TgCyxrzRdcI/AAAAAAAAAHU/4Bmo92PTpnk/s1600/classes-examples.png" imageanchor="1"&gt;&lt;img border="0" height="278" src="http://2.bp.blogspot.com/-kOEhquI1WcY/TgCyxrzRdcI/AAAAAAAAAHU/4Bmo92PTpnk/s1600/classes-examples.png" width="600" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;What is missing?&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Create Category objects. Should an adminstrator to this or should it happen automatically? This needs to be discussed with the client.&lt;/li&gt;&lt;li&gt;Set link between Category and Auction. This should be done by Create auction.&lt;/li&gt;&lt;li&gt;Read User objects. What do we need the email for?&lt;/li&gt;&lt;li&gt;Read Bid objects. This should be part of Auction details. We should define an example to show this.&lt;/li&gt;&lt;li&gt;Update attributes. This needs to be discussed with the client.&lt;/li&gt;&lt;li&gt;Delete objects. We need to discuss with the client if this should be possible.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Based on this analysis, I add a Category parameter to the first example and use that parameter to find an existing Category object. I create a link between this Category and the new Auction:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-4Fo6qZLw94A/TgCzPJgZkqI/AAAAAAAAAHc/ULgjIbWgdDw/s1600/Create%2Bauction%2Bobjects%2B2.png" imageanchor="1"&gt;&lt;img border="0" height="122" src="http://4.bp.blogspot.com/-4Fo6qZLw94A/TgCzPJgZkqI/AAAAAAAAAHc/ULgjIbWgdDw/s1600/Create%2Bauction%2Bobjects%2B2.png" width="600" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;Implement JPA classes&lt;/h3&gt;It's straight forward to implement the classes as JPA entities.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;@Entity @Table(name="tb_auction")&lt;br /&gt;public class Auction {&lt;br /&gt;    @Id @GeneratedValue(strategy=GenerationType.AUTO)&lt;br /&gt;    private Long id;&lt;br /&gt;&lt;br /&gt;    private String title;&lt;br /&gt;    private float startingPrice;&lt;br /&gt;    private String description;&lt;br /&gt; &lt;br /&gt;    @ManyToOne(optional=false)&lt;br /&gt;    private Category category;&lt;br /&gt;&lt;br /&gt;    @ManyToOne(optional=false)&lt;br /&gt;    private User seller;&lt;br /&gt;&lt;br /&gt;    @OneToMany(mappedBy="auction")&lt;br /&gt;    private Set&amp;lt;Bid&amp;gt; bids = new HashSet&amp;lt;Bid&amp;gt;();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@Entity @Table(name="tb_bid")&lt;br /&gt;public class Bid {&lt;br /&gt;    @Id @GeneratedValue(strategy=GenerationType.AUTO)&lt;br /&gt;    private Long id;&lt;br /&gt;&lt;br /&gt;    private float bid;&lt;br /&gt;&lt;br /&gt;    @ManyToOne(optional=false)&lt;br /&gt;    private Auction auction;&lt;br /&gt; &lt;br /&gt;    @ManyToOne(optional=false)&lt;br /&gt;    private User bidder;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@Entity @Table(name="tb_category")&lt;br /&gt;public class Category {&lt;br /&gt;    @Id @GeneratedValue(strategy=GenerationType.AUTO)&lt;br /&gt;    private Long id;&lt;br /&gt;&lt;br /&gt;    private String name;&lt;br /&gt;    &lt;br /&gt;    @OneToMany(mappedBy="category")&lt;br /&gt;    private Set&amp;lt;Auction&amp;gt; auctions = new HashSet&amp;lt;Auction&amp;gt;();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@Entity @Table(name="tb_user")&lt;br /&gt;public class User {&lt;br /&gt;    @Id @GeneratedValue (strategy=GenerationType.AUTO)&lt;br /&gt;    private Long id;&lt;br /&gt;&lt;br /&gt;    private String email;&lt;br /&gt;    &lt;br /&gt;    @OneToMany(mappedBy="seller")&lt;br /&gt;    private Set&amp;lt;Auction&amp;gt; auctions = new HashSet&amp;lt;Auction&amp;gt;();&lt;br /&gt;&lt;br /&gt;    @OneToMany(mappedBy="bidder")&lt;br /&gt;    private Set&amp;lt;Bid&amp;gt; bids = new HashSet&amp;lt;Bid&amp;gt;();&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;(Getters and setters removed for clarity.)&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Implement tests&lt;/h3&gt;Each example is a test. I divide each test into 4 parts:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Initial objects&lt;/li&gt;&lt;li&gt;Input&lt;/li&gt;&lt;li&gt;Output&lt;/li&gt;&lt;li&gt;Result objects&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;With SmartGWT and JPA this becomes:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Initial objects: Create objects in the database. The best way to do this in a unit test is to use a HSQLDB in-memory database, which can create the database schema on the fly.&lt;/li&gt;&lt;li&gt;Input: Call a SmartGWT datasource with the specified parameters.&lt;/li&gt;&lt;li&gt;Output: Check the output from the datasource.&lt;/li&gt;&lt;li&gt;Result objects: Check the resulting objects in the database.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Let's start with testing the first example: Jane wants to sell an electric guitar.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-4Fo6qZLw94A/TgCzPJgZkqI/AAAAAAAAAHc/ULgjIbWgdDw/s1600/Create%2Bauction%2Bobjects%2B2.png" imageanchor="1"&gt;&lt;img border="0" height="122" src="http://4.bp.blogspot.com/-4Fo6qZLw94A/TgCzPJgZkqI/AAAAAAAAAHc/ULgjIbWgdDw/s1600/Create%2Bauction%2Bobjects%2B2.png" width="600" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public class AuctionTest extends TestCase {&lt;br /&gt; private DataSource auctionDS;&lt;br /&gt; private DataSource bidDS;&lt;br /&gt; &lt;br /&gt; private HashMap&amp;lt;String,Object&amp;gt; input = new HashMap&amp;lt;String,Object&amp;gt;();&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; protected void setUp() throws Exception {&lt;br /&gt;  super.setUp();&lt;br /&gt;  auctionDS = DataSourceManager.get("Auction");&lt;br /&gt;  bidDS = DataSourceManager.get("Bid");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void testJaneWantsToSellAnElectricGuitar() throws Exception {&lt;br /&gt;  // Initial objects:&lt;br /&gt;  category1 = new Category();&lt;br /&gt;  category1.setName("Musical instruments");&lt;br /&gt;  persist(category1); &lt;br /&gt;&lt;br /&gt;  // Input:&lt;br /&gt;  input.put("title",       "Electric guitar for beginners");&lt;br /&gt;  input.put("startingPrice",  150f);&lt;br /&gt;  input.put("description", "Nice electric guitar with amplifier.");&lt;br /&gt;  input.put("email",       "jane99@abc123.com");&lt;br /&gt;  input.put("categoryId",  category1.getId());&lt;br /&gt;  auctionDS.add(input);&lt;br /&gt;&lt;br /&gt;  // Output: None.&lt;br /&gt;&lt;br /&gt;  // Result objects:&lt;br /&gt;  List&lt;user&gt; users = getResultObjects(User.class);&lt;br /&gt;  assertEquals(1, users.size());&lt;br /&gt;  assertEquals("jane99@abc123.com", users.get(0).getEmail());&lt;br /&gt;&lt;br /&gt;  List&lt;auction&gt; auctions = getResultObjects(Auction.class);&lt;br /&gt;  assertEquals(1, auctions.size());&lt;br /&gt;  Auction auction = auctions.get(0);&lt;br /&gt;  assertEquals("Electric guitar for beginners",        auction.getTitle());&lt;br /&gt;  assertEquals(150f,                                   auction.getStartingPrice());&lt;br /&gt;  assertEquals("Nice electric guitar with amplifier.", auction.getDescription());&lt;br /&gt;  assertEquals(category1.getId().intValue(),           auction.getCategoryId());&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private void persist(Object... objects) throws Exception {&lt;br /&gt;  EntityManager em = EMF.getEntityManager();&lt;br /&gt;  Object tx = EMF.getTransaction(em);&lt;br /&gt;  for(Object obj : objects) {&lt;br /&gt;   em.persist(obj);&lt;br /&gt;  }&lt;br /&gt;  EMF.commitTransaction(tx);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private &amp;lt;T&amp;gt; List&lt;t&gt; getResultObjects(Class&amp;lt;T&amp;gt; clazz) throws Exception {&lt;br /&gt;  EntityManager em = EMF.getEntityManager();&lt;br /&gt;  Object tx = EMF.getTransaction(em);&lt;br /&gt;  List&amp;lt;T&amp;gt; result = em.createQuery("from " + clazz.getName()).getResultList();&lt;br /&gt;  EMF.commitTransaction(tx);&lt;br /&gt;  return result;&lt;br /&gt; }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Here is the second example: Bob wants to buy an electric guitar. This is more complicated with some initial objects and several steps.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-A_EuVma88c8/TgCxuzE5SmI/AAAAAAAAAG0/vtlvGLYiGnk/s1600/Search%2Bfor%2Bauctions%2Bobjects%2B2.png" imageanchor="1"&gt;&lt;img border="0" height="164" src="http://3.bp.blogspot.com/-A_EuVma88c8/TgCxuzE5SmI/AAAAAAAAAG0/vtlvGLYiGnk/s1600/Search%2Bfor%2Bauctions%2Bobjects%2B2.png" width="600" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-uS1slCAqWsM/TgCyBiAWovI/AAAAAAAAAG8/64DE-FpHzjI/s1600/Auction%2Bdetails%2Bobjects.png" imageanchor="1"&gt;&lt;img border="0" height="136" src="http://3.bp.blogspot.com/-uS1slCAqWsM/TgCyBiAWovI/AAAAAAAAAG8/64DE-FpHzjI/s1600/Auction%2Bdetails%2Bobjects.png" width="600" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-K5yRmbXiqaY/TgCyR6s9CeI/AAAAAAAAAHE/OvEUreirbb4/s1600/Place%2Bbid%2Bobjects.png" imageanchor="1"&gt;&lt;img border="0" height="190" src="http://3.bp.blogspot.com/-K5yRmbXiqaY/TgCyR6s9CeI/AAAAAAAAAHE/OvEUreirbb4/s1600/Place%2Bbid%2Bobjects.png" width="600" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public void testBobWantsToBuyAnElectricGuitar() throws Exception {&lt;br /&gt;  initialObjects2Auctions();&lt;br /&gt;  &lt;br /&gt;  // Search for auctions.&lt;br /&gt;  input.put("categoryId", category1.getId());&lt;br /&gt;  input.put("title",      "guitar");&lt;br /&gt;  input.put("maxPrice",   250f);&lt;br /&gt;  &lt;br /&gt;  List&amp;lt;Auction&amp;gt; output = auctionDS.fetch(input);&lt;br /&gt;  assertEquals(2, output.size());&lt;br /&gt;  assertEquals("Guitar",          output.get(0).getTitle());&lt;br /&gt;  assertEquals(200f,              output.get(0).getStartingPrice());&lt;br /&gt;  assertEquals("Electric guitar", output.get(1).getTitle());&lt;br /&gt;  assertEquals(150f,              output.get(1).getStartingPrice());&lt;br /&gt;&lt;br /&gt;  // Auction details.&lt;br /&gt;  input.clear();&lt;br /&gt;  input.put("id", auction1.getId());&lt;br /&gt;  &lt;br /&gt;  output = auctionDS.fetch(input);&lt;br /&gt;  assertEquals(1, output.size());&lt;br /&gt;  Auction auction = output.get(0);&lt;br /&gt;  assertEquals("Electric guitar for beginners",        auction.getTitle());&lt;br /&gt;  assertEquals("Nice electric guitar with amplifier.", auction.getDescription());&lt;br /&gt;  assertEquals(150f,                                   auction.getStartingPrice());&lt;br /&gt;  assertEquals("No bids.",                             auction.getHighestBid());&lt;br /&gt;&lt;br /&gt;  // Place bid.&lt;br /&gt;  input.clear();&lt;br /&gt;  input.put("auctionId",   auction1.getId());&lt;br /&gt;  input.put("bidderEmail", "bob@abc999.com");&lt;br /&gt;  input.put("bid",         150);&lt;br /&gt;  ErrorReport errors = bidDS.validate(input, true);&lt;br /&gt;  assertEquals(null, errors);&lt;br /&gt;  bidDS.add(input);&lt;br /&gt;&lt;br /&gt;  // Result objects:&lt;br /&gt;  List&amp;lt;User&amp;gt; users = getResultObjects(User.class);&lt;br /&gt;  assertEquals(1, users.size());&lt;br /&gt;  User user = users.get(0);&lt;br /&gt;  assertEquals("bob@abc999.com", user.getEmail());&lt;br /&gt;  List&amp;lt;Bid&amp;gt; bids = getResultObjects(Bid.class);&lt;br /&gt;  assertEquals(1, bids.size());&lt;br /&gt;  Bid bid = bids.get(0);&lt;br /&gt;  assertEquals(150,              bid.getBid());&lt;br /&gt;  assertEquals(user.getId(),     bid.getBidder().getId());&lt;br /&gt;  assertEquals(auction1.getId(), bid.getAuction().getId());&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; private void initialObjects2Auctions() throws Exception {&lt;br /&gt;  category1 = new Category();&lt;br /&gt;  category1.setName("Musical instruments");&lt;br /&gt;  persist(category1);&lt;br /&gt;&lt;br /&gt;  auction1 = initialAuction(category1, "Electric guitar for beginners", "Nice electric guitar with amplifier.", 150f);&lt;br /&gt;  auction2 = initialAuction(category1, "Guitar", "Used guitar.", 250f);&lt;br /&gt;  persist(auction1, auction2);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private Auction initialAuction(Category category, String title, String description, float startingPrice) {&lt;br /&gt;  Auction object = new Auction();&lt;br /&gt;  object.setTitle(title);&lt;br /&gt;  object.setDescription(description);&lt;br /&gt;  object.setStartingPrice(startingPrice);&lt;br /&gt;  category.addAuction(object);  &lt;br /&gt;  return object;&lt;br /&gt; }&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Let's add some more tests while we're at it:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public void testNoSearchCriteria() throws Exception {&lt;br /&gt;  initialObjects2Auctions();&lt;br /&gt;  assertEquals(2, auctionDS.fetch(input).size());&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void testSearchForUnknownCategory() throws Exception {&lt;br /&gt;  initialObjects2Auctions();&lt;br /&gt;  input.put("categoryId", category1.getId() + 1);&lt;br /&gt;  assertEquals(0, auctionDS.fetch(input).size());&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void testSearchForMaxPrice150() throws Exception {&lt;br /&gt;  initialObjects2Auctions();&lt;br /&gt;  input.put("maxPrice", 150f);&lt;br /&gt;  assertEquals(1, auctionDS.fetch(input).size());&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void testSearchForMaxPrice250() throws Exception {&lt;br /&gt;  initialObjects2Auctions();&lt;br /&gt;  input.put("maxPrice", 250f);&lt;br /&gt;  assertEquals(2, auctionDS.fetch(input).size());&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void testSearchForTitlePiano() throws Exception {&lt;br /&gt;  initialObjects2Auctions();&lt;br /&gt;  input.put("title", "piano");&lt;br /&gt;  assertEquals(0, auctionDS.fetch(input).size());&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void testSearchForTitleElectricGuitar() throws Exception {&lt;br /&gt;  initialObjects2Auctions();&lt;br /&gt;  input.put("title", "electric guitar");&lt;br /&gt;  assertEquals(1, auctionDS.fetch(input).size());&lt;br /&gt; }&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;These tests will actually fail, but that's ok. When I fix them, I will learn much about the behavior.&lt;br /&gt;&lt;br /&gt;The first thing I notice, is a compilation error because of a missing method, so let's add it:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public class Auction {&lt;br /&gt; public String getHighestBid() {&lt;br /&gt;  Float highestBid = null;&lt;br /&gt;  for(Bid bid : getBids()) {&lt;br /&gt;   if(highestBid == null || bid.getBid() &amp;gt; highestBid) {&lt;br /&gt;    highestBid = bid.getBid();&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return (highestBid != null ? highestBid.toString() : "No bids.");&lt;br /&gt; }&lt;br /&gt; ...&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This can instead be handled by a database query in the "serverObject" that will be implemented later in this article.&lt;br /&gt;&lt;br /&gt;To use HSQLDB for unit testing, add hsqldb.jar to the project and create a persistence.xml file in the test/META-INF directory with the following properties:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;property name="hibernate.connection.url" value="jdbc:hsqldb:mem:auction"/&amp;gt;&lt;br /&gt;&amp;lt;property name="hibernate.hbm2ddl.auto" value="create-drop"/&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h3&gt;Implement datasources&lt;/h3&gt;The tests use the datasources "Bid" and "Auction" that I haven't defined yet. Here is a starting point:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;DataSource ID="Auction"&lt;br /&gt;    serverConstructor="com.isomorphic.jpa.JPADataSource"&lt;br /&gt;    beanClassName="com.objectgeneration.auctions.Auction"&amp;gt;&lt;br /&gt;    &amp;lt;fields&amp;gt;&lt;br /&gt;        &amp;lt;field name="id"          type="sequence" hidden="true"       primaryKey="true" /&amp;gt;&lt;br /&gt;        &amp;lt;field name="title"       type="text"     title="Title"       required="true"   /&amp;gt;&lt;br /&gt;        &amp;lt;field name="description" type="text"     title="Description" required="true" /&amp;gt;&lt;br /&gt;        &amp;lt;field name="categoryId"  type="integer"  title="Category"    canEdit="false" /&amp;gt;&lt;br /&gt;    &amp;lt;/fields&amp;gt;&lt;br /&gt;&amp;lt;/DataSource&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;DataSource ID="Bid"&lt;br /&gt;    serverConstructor="com.isomorphic.jpa.JPADataSource"&lt;br /&gt;    beanClassName="com.objectgeneration.auctions.Bid"&amp;gt;&lt;br /&gt;    &amp;lt;fields&amp;gt;&lt;br /&gt;        &amp;lt;field name="id"   type="sequence" hidden="true" primaryKey="true"/&amp;gt;&lt;br /&gt;        &amp;lt;field name="bid"  type="number"   title="Bid"   required="true"/&amp;gt;&lt;br /&gt;    &amp;lt;/fields&amp;gt;&lt;br /&gt;&amp;lt;/DataSource&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h3&gt;Using SmartGWT and JPA from JUnit&lt;/h3&gt;I am getting eager to implement the behavior, but unfortunately I have to fix some issues to get SmartGWT to work with JPA in JUnit first. Let's start:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-Kg6zQrb1tEA/TgC2fxrRtfI/AAAAAAAAAHk/5O8Reb9_Wyk/s1600/junit1.png" imageanchor="1"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-Kg6zQrb1tEA/TgC2fxrRtfI/AAAAAAAAAHk/5O8Reb9_Wyk/s1600/junit1.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;That didn't go so well. Here is a stack trace:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;java.lang.NullPointerException&lt;br /&gt; at com.isomorphic.io.ISCFile.&amp;lt;init&amp;gt;(ISCFile.java:145)&lt;br /&gt; ...&lt;br /&gt; at com.isomorphic.datasource.DataSourceManager.get(DataSourceManager.java:68)&lt;br /&gt; at com.objectgeneration.auctions.AuctionTest.setUp(AuctionTest.java:31)&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The problem here is that SmartGWT thinks it is running as a servlet, but it does not find its web root. This can be configured in server.properties. Create a server.properties file for unit testing in the test folder. The content should be similar to the production version, except for the following:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;# This is user specific. Use Ant filtering to generate this.&lt;br /&gt;webRoot: /Users/lars/temp/auctions/war/&lt;br /&gt;&lt;br /&gt;jpa.emfProvider: com.isomorphic.jpa.EMFProviderLMT&lt;br /&gt;&lt;br /&gt;# Name of the datasource (from persistence.xml)&lt;br /&gt;jpa.persistenceUnitName: ds&lt;/code&gt;&lt;/pre&gt;Also make sure the test source has a separate output folder (see Eclipse: Project &amp;gt; Properties &amp;gt; Java Build Path &amp;gt; Source). Otherwise the server.properties from src and test will overwrite each other in the output directory with random results.&lt;br /&gt;&lt;br /&gt;Running JUnit again, we get the next problem:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.util.Map&lt;br /&gt; at com.isomorphic.datasource.BasicDataSource.buildFieldData(BasicDataSource.java:426)&lt;br /&gt; ...&lt;br /&gt; at com.isomorphic.datasource.DataSourceManager.get(DataSourceManager.java:68)&lt;br /&gt; at com.objectgeneration.auctions.AuctionTest.setUp(AuctionTest.java:31)&lt;/pre&gt;This can be fixed by another setting in test/server.properties:&lt;br /&gt;&lt;pre&gt;isomorphicPathRootRelative: Auctions_js/sc&lt;/code&gt;&lt;/pre&gt;(It should point to the sub-directory of war/ where initsc.js is created.)&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;JPA relations and SmartGWT&lt;/h3&gt;&lt;br /&gt;Trying again I get this stack trace:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;javax.persistence.PersistenceException: org.hibernate.PropertyValueException: not-null property references a null or transient value: com.smartgwt.sample.server.Auction.category&lt;br /&gt; at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:637)&lt;br /&gt; at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:226)&lt;br /&gt; at com.isomorphic.jpa.JPADataSource.executeAdd(JPADataSource.java:452)&lt;br /&gt; at com.isomorphic.datasource.DataSource.execute(DataSource.java:1050)&lt;br /&gt; at com.isomorphic.jpa.JPADataSource.execute(JPADataSource.java:218)&lt;br /&gt; …&lt;br /&gt; at com.isomorphic.datasource.DataSource.add(DataSource.java:1948)&lt;br /&gt; at com.objectgeneration.auctions.AuctionTest.testJaneWantsToSellAnElectricGuitar(AuctionTest.java:45)&lt;br /&gt;Caused by: org.hibernate.PropertyValueException: not-null property references a null or transient value: com.smartgwt.sample.server.Auction.category&lt;/code&gt;&lt;/pre&gt;SmartGWT does not handle JPA relations like "Category category" in class Auction, it only handles foreign keys like "long categoryId". But I want to use JPA relations. The workaround is to have both and define only the foreign key in the datasource as above. The Java code is as follows:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public class Auction {&lt;br /&gt; @ManyToOne(optional=false)&lt;br /&gt; @JoinColumn(name="category", nullable=false, insertable=false, updatable=false)&lt;br /&gt; private Category category;&lt;br /&gt; &lt;br /&gt; @Column(name="category")&lt;br /&gt; private long categoryId;&lt;br /&gt;...&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;The category column is mapped to 2 Java variables, but only one of them can be writable. Both of them need to be set when creating the initial objects:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public class Category {&lt;br /&gt; public void addAuction(Auction auction) {&lt;br /&gt;  auction.setCategory(this);&lt;br /&gt;  getAuctions().add(auction);&lt;br /&gt;  assert getId() != null;&lt;br /&gt;  auction.setCategoryId(getId());&lt;br /&gt; }&lt;br /&gt;...&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;Trying again:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;javax.persistence.EntityExistsException: org.hibernate.exception.ConstraintViolationException: could not insert: [com.smartgwt.sample.server.Auction]&lt;br /&gt; …&lt;br /&gt; at com.isomorphic.datasource.DataSource.add(DataSource.java:1948)&lt;br /&gt; at com.objectgeneration.auctions.AuctionTest.testJaneWantsToSellAnElectricGuitar(AuctionTest.java:45)&lt;br /&gt;Caused by: org.hibernate.exception.ConstraintViolationException: could not insert: [com.smartgwt.sample.server.Auction]&lt;br /&gt; ...&lt;br /&gt;Caused by: java.sql.SQLException: Integrity constraint violation - no parent FKD896457230F353B7 table: TB_USER in statement [insert into tb_auction (id, category, description, seller, startingPrice, title) values (null, ?, ?, ?, ?, ?)]&lt;/code&gt;&lt;/pre&gt;Now we are finally getting to the business logic. I cannot create an Auction without a User.&lt;br /&gt;&lt;br /&gt;I decide to create a new User datasource and add a User before creating the Auction:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public void testJaneWantsToSellAnElectricGuitar() throws Exception {&lt;br /&gt;  // Initial objects:&lt;br /&gt;  category1 = new Category();&lt;br /&gt;  category1.setName("Musical instruments");&lt;br /&gt;  persist(category1);&lt;br /&gt;&lt;br /&gt;  // Input:&lt;br /&gt;  long userId = createUser("jane99@abc123.com");&lt;br /&gt;  input.put("title",       "Electric guitar for beginners");&lt;br /&gt;  input.put("startingPrice",  150f);&lt;br /&gt;  input.put("description", "Nice electric guitar with amplifier.");&lt;br /&gt;  input.put("categoryId",  category1.getId());&lt;br /&gt;  input.put("sellerId",    userId);&lt;br /&gt;  auctionDS.add(input);&lt;br /&gt;&lt;br /&gt;  ...&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private long createUser(String email) throws Exception {&lt;br /&gt;  HashMap&amp;lt;String,object&amp;gt; input = new HashMap&amp;lt;String,object&amp;gt;();&lt;br /&gt;  input.put("email", email);&lt;br /&gt;  ErrorReport errors = userDS.validate(input, true);&lt;br /&gt;  assertEquals(null, errors);&lt;br /&gt;  DSRequest dsRequest = new DSRequest(userDS.getName(), DataSource.OP_ADD);&lt;br /&gt;  dsRequest.setValues(input);&lt;br /&gt;  DSResponse dsResponse = dsRequest.execute();&lt;br /&gt;  return ((User)dsResponse.getData()).getId();&lt;br /&gt; }&lt;/code&gt;&lt;/pre&gt;I also connect the initial Auction objects in the other unit tests to a User. This makes some of the tests pass:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-55czj21fQII/TgC4RgQEXCI/AAAAAAAAAHs/SYqWm5U6ekY/s1600/junit3.png" imageanchor="1"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-55czj21fQII/TgC4RgQEXCI/AAAAAAAAAHs/SYqWm5U6ekY/s1600/junit3.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Many of these failures are because the tests are not isolated. If I run each test separately, many of them will pass. The reason for this is that the database is created when the first test is run, and then the next test uses the same database with the objects that are left. I need to empty the database between each test. This is easy to do in JPA; just call EntityManagerFactory.close(). But I don't have access to the EntityManagerFactory that SmartGWT uses. To fix this, I need to implement my own "EMF provider" and point to it in test/server.properties:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;jpa.emfProvider: com.objectgeneration.auctions.server.MyEMFProvider&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;And here is the implementation:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;import javax.persistence.EntityManager;&lt;br /&gt;import javax.persistence.EntityManagerFactory;&lt;br /&gt;import javax.persistence.EntityTransaction;&lt;br /&gt;import javax.persistence.Persistence;&lt;br /&gt;&lt;br /&gt;import com.isomorphic.jpa.EMFProviderInterface;&lt;br /&gt;&lt;br /&gt;public class MyEMFProvider implements EMFProviderInterface {&lt;br /&gt; private static EntityManagerFactory factory;&lt;br /&gt; &lt;br /&gt; // Support multi-threaded tests.&lt;br /&gt; private static ThreadLocal&amp;lt;Entitymanager&amp;gt; entityManager = new ThreadLocal&amp;lt;Entitymanager&amp;gt;();&lt;br /&gt; &lt;br /&gt; public MyEMFProvider() {&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /** Close the factory after each unit test so it starts with an empty database next time. */&lt;br /&gt; public static void close() {&lt;br /&gt;  if(entityManager.get() != null) {&lt;br /&gt;   entityManager.get().close();&lt;br /&gt;   entityManager.set(null);&lt;br /&gt;  }&lt;br /&gt;  if(factory != null) {&lt;br /&gt;   factory.close();&lt;br /&gt;   factory = null;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; protected EntityManagerFactory createFactory() {&lt;br /&gt;  return Persistence.createEntityManagerFactory("ds");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public EntityManagerFactory get() {&lt;br /&gt;  if(factory == null) {&lt;br /&gt;   factory = createFactory();&lt;br /&gt;  }&lt;br /&gt;  return factory;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public EntityManager getEntityManager() {&lt;br /&gt;  if(entityManager.get() == null) {&lt;br /&gt;   entityManager.set(get().createEntityManager());&lt;br /&gt;  }&lt;br /&gt;  return entityManager.get();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void returnEntityManager(EntityManager em) {&lt;br /&gt;  // Do nothing.&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public Object getTransaction(EntityManager em) {&lt;br /&gt;  EntityTransaction tx = em.getTransaction();&lt;br /&gt;  if(!tx.isActive()) {&lt;br /&gt;   tx.begin();&lt;br /&gt;  }&lt;br /&gt;  return tx;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void commitTransaction(Object obj) {&lt;br /&gt;  EntityTransaction tx = (EntityTransaction) obj;&lt;br /&gt;  if(tx.isActive()) {&lt;br /&gt;   tx.commit();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public void rollbackTransaction(Object obj) {&lt;br /&gt;  EntityTransaction tx = (EntityTransaction) obj;&lt;br /&gt;  if(tx.isActive()) {&lt;br /&gt;   tx.rollback();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;Add the following to the test class to start with an empty database in each unit test:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;protected void tearDown() throws Exception {&lt;br /&gt;  MyEMFProvider.close();&lt;br /&gt;  super.tearDown();&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;This takes us much closer to our goal, and we can continue with the business logic:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-XEhqMO5afwc/TgC4oMb40zI/AAAAAAAAAH0/EgdHT_idBsU/s1600/junit4.png" imageanchor="1"&gt;&lt;img border="0" height="242" src="http://3.bp.blogspot.com/-XEhqMO5afwc/TgC4oMb40zI/AAAAAAAAAH0/EgdHT_idBsU/s400/junit4.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;Implement datasource&lt;/h3&gt;There is no wonder that maxPrice is not working, the Auction class does not even have this field. I want to find all Auctions where Auction.startingPrice &amp;lt;= the maxPrice parameter. To implement this, I need to implement special handling for this data source in a "serverObject":&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;DataSource ID="Auction"&lt;br /&gt;    serverConstructor="com.isomorphic.jpa.JPADataSource"&lt;br /&gt;    beanClassName="com.smartgwt.sample.server.Auction"&amp;gt;&lt;br /&gt;    &amp;lt;serverObject className="com.objectgeneration.auctions.AuctionRequestHandler"/&amp;gt;&lt;br /&gt;    &amp;lt;fields&amp;gt;&lt;br /&gt;        &amp;lt;field name="id"          type="sequence" hidden="true"   primaryKey="true" /&amp;gt;&lt;br /&gt;        &amp;lt;field name="title"       type="text"     title="Title"       required="true"   /&amp;gt;&lt;br /&gt;        &amp;lt;field name="description" type="text"     title="Description" required="true" /&amp;gt;&lt;br /&gt;        &amp;lt;field name="categoryId"  type="integer"  title="Category"    canEdit="false" /&amp;gt;&lt;br /&gt;        &lt;br /&gt;        &amp;lt;!-- This field does not exist in the Auction class. It is implemented in AuctionRequestHandler. --&amp;gt;&lt;br /&gt;        &amp;lt;field name="maxPrice"    type="number"   title="Max price" hidden="true" /&amp;gt;&lt;br /&gt;    &amp;lt;/fields&amp;gt;&lt;br /&gt;&amp;lt;/DataSource&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The serverObject in the XML above refers to a class that will be called instead of the default SmartGWT handler, and there I can handle maxPrice:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;import java.util.List;&lt;br /&gt;import java.util.Map;&lt;br /&gt;&lt;br /&gt;import javax.persistence.EntityManager;&lt;br /&gt;import javax.persistence.Query;&lt;br /&gt;&lt;br /&gt;import org.apache.log4j.Logger;&lt;br /&gt;&lt;br /&gt;import com.isomorphic.datasource.DSRequest;&lt;br /&gt;import com.isomorphic.datasource.DSResponse;&lt;br /&gt;import com.isomorphic.jpa.EMF;&lt;br /&gt;import com.smartgwt.sample.server.Auction;&lt;br /&gt;&lt;br /&gt;public class AuctionRequestHandler {&lt;br /&gt; private static final Logger log = Logger.getLogger(AuctionRequestHandler.class);&lt;br /&gt; &lt;br /&gt; public DSResponse fetch(DSRequest dsRequest) throws Exception {&lt;br /&gt;  Map&amp;lt;String,Object&amp;gt; criteria = dsRequest.getCriteria();&lt;br /&gt;  log.info("fetch(" + criteria + ")");&lt;br /&gt;  &lt;br /&gt;  String queryString = "from Auction";&lt;br /&gt;  String separator = " where ";&lt;br /&gt;  for(String paramName : criteria.keySet()) {&lt;br /&gt;   queryString += separator;&lt;br /&gt;   if(paramName.equals("maxPrice")) {&lt;br /&gt;    queryString += "startingPrice &amp;lt;= :" + paramName;&lt;br /&gt;   } else {&lt;br /&gt;    queryString += paramName + " = :" + paramName;&lt;br /&gt;   }&lt;br /&gt;   separator = " and ";&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  EntityManager em = EMF.getEntityManager();&lt;br /&gt;  Query query = em.createQuery(queryString);&lt;br /&gt;  for(String paramName : criteria.keySet()) {&lt;br /&gt;   Object paramValue = criteria.get(paramName);&lt;br /&gt;   query.setParameter(paramName, paramValue);&lt;br /&gt;  }&lt;br /&gt;  List&amp;lt;Auction&amp;gt; result = query.getResultList();&lt;br /&gt;  return new DSResponse(result, DSResponse.STATUS_SUCCESS);&lt;br /&gt; }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-6vQ7g_L7W1Y/TgC55uIoVmI/AAAAAAAAAH8/RRV_0zdyj-o/s1600/junit5.png" imageanchor="1"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-6vQ7g_L7W1Y/TgC55uIoVmI/AAAAAAAAAH8/RRV_0zdyj-o/s1600/junit5.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;I want to search for title with substrings and ignore case. Substrings is built in to SmartGWT, but not ignore case. Here is an updated version with special handling for the title field:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;public DSResponse fetch(DSRequest dsRequest) throws Exception {&lt;br /&gt;  Map&amp;lt;String,Object&amp;gt; criteria = dsRequest.getCriteria();&lt;br /&gt;  log.info("fetch(" + criteria + ")");&lt;br /&gt;  &lt;br /&gt;  String queryString = "from Auction";&lt;br /&gt;  String separator = " where ";&lt;br /&gt;  for(String paramName : criteria.keySet()) {&lt;br /&gt;   Object paramValue = criteria.get(paramName);&lt;br /&gt;   queryString += separator;&lt;br /&gt;   if(paramName.equals("maxPrice")) {&lt;br /&gt;    queryString += "startingPrice &amp;lt;= :" + paramName;&lt;br /&gt;   } else if(paramName.equals("title")) {&lt;br /&gt;    queryString += "UPPER(" + paramName + ") LIKE '%" + ((String)paramValue).toUpperCase() + "%'";&lt;br /&gt;   } else {&lt;br /&gt;    queryString += paramName + " = :" + paramName;&lt;br /&gt;   }&lt;br /&gt;   separator = " and ";&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  EntityManager em = EMF.getEntityManager();&lt;br /&gt;  Query query = em.createQuery(queryString);&lt;br /&gt;  for(String paramName : criteria.keySet()) {&lt;br /&gt;   if(!paramName.equals("title")) {&lt;br /&gt;    Object paramValue = criteria.get(paramName);&lt;br /&gt;    query.setParameter(paramName, paramValue);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  List&lt;auction&gt; result = query.getResultList();&lt;br /&gt;  return new DSResponse(result, DSResponse.STATUS_SUCCESS);&lt;br /&gt; }&lt;/code&gt;&lt;/pre&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-wfjETyyq5HM/TgC6KCMXnII/AAAAAAAAAIE/OAbMHXwEx-0/s1600/junit7.png" imageanchor="1"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-wfjETyyq5HM/TgC6KCMXnII/AAAAAAAAAIE/OAbMHXwEx-0/s1600/junit7.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Success!&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Wrapping it up&lt;/h2&gt;One improvement could be to move the getHighestBid() logic to a database query. The AuctionRequestHandler could call this query and store the value in a @Transient field in the Auction class to send it to the client, or it could send HashMaps instead of Auction objects.&lt;br /&gt;&lt;br /&gt;We also need to handle the things that SmartGWT does by default: Transactions, pagination and sorting. (I believe that if you don't implement pagination and sorting in the server, the client will handle it instead. This may be acceptable if you don't have thousands of rows in that database table. Please refer to the SmartGWT documentation.)&lt;br /&gt;&lt;br /&gt;A complete solution with a general superclass and special handling in a subclass looks like this:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;import java.util.LinkedHashMap;&lt;br /&gt;import java.util.List;&lt;br /&gt;import java.util.Map;&lt;br /&gt;&lt;br /&gt;import javax.persistence.EntityManager;&lt;br /&gt;import javax.persistence.EntityTransaction;&lt;br /&gt;import javax.persistence.Query;&lt;br /&gt;&lt;br /&gt;import org.apache.log4j.Logger;&lt;br /&gt;&lt;br /&gt;import com.isomorphic.datasource.DSRequest;&lt;br /&gt;import com.isomorphic.datasource.DSResponse;&lt;br /&gt;import com.isomorphic.jpa.EMF;&lt;br /&gt;&lt;br /&gt;public class RequestHandler {&lt;br /&gt; private static final Logger log = Logger.getLogger(RequestHandler.class);&lt;br /&gt;&lt;br /&gt; public DSResponse fetch(DSRequest dsRequest) throws Exception {&lt;br /&gt;  return handleRequest(dsRequest);&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public DSResponse add(DSRequest dsRequest) throws Exception {&lt;br /&gt;  return handleRequest(dsRequest);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public DSResponse remove(DSRequest dsRequest) throws Exception {&lt;br /&gt;  return handleRequest(dsRequest);&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public DSResponse update(DSRequest dsRequest) throws Exception {&lt;br /&gt;  return handleRequest(dsRequest);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; protected DSResponse handleRequest(DSRequest dsRequest) throws Exception {&lt;br /&gt;  EntityManager em = EMF.getEntityManager();&lt;br /&gt;  EntityTransaction tx = (EntityTransaction) EMF.getTransaction(em);&lt;br /&gt;  try {&lt;br /&gt;   DSResponse dsResponse = handleInsideTransaction(dsRequest);&lt;br /&gt;   tx.commit();&lt;br /&gt;   return dsResponse;&lt;br /&gt;  } catch(Exception e) {&lt;br /&gt;   MyEMFProvider.rollback(tx);&lt;br /&gt;   throw e;&lt;br /&gt;  } finally {&lt;br /&gt;   MyEMFProvider.finish();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; protected DSResponse handleInsideTransaction(DSRequest dsRequest) throws Exception {&lt;br /&gt;  return dsRequest.execute();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @SuppressWarnings("unchecked")&lt;br /&gt; protected &amp;lt;T&amp;gt; DSResponse query(EntityManager em, DSRequest dsRequest, Class&amp;lt;T&amp;gt; clazz) {&lt;br /&gt;  String queryString = "from " + clazz.getName();&lt;br /&gt;&lt;br /&gt;  LinkedHashMap&amp;lt;String, Object&amp;gt; parameters = new LinkedHashMap&amp;lt;String, Object&amp;gt;();&lt;br /&gt;  queryString = addCriteria(queryString, dsRequest.getCriteria(), parameters);&lt;br /&gt;  &lt;br /&gt;  List&amp;lt;T&amp;gt; result = selectObjects(em, dsRequest, queryString, parameters);&lt;br /&gt;  long totalRows = countObjects(em, dsRequest, queryString, parameters, result);&lt;br /&gt;&lt;br /&gt;  DSResponse dsResponse = new DSResponse(result, DSResponse.STATUS_SUCCESS);&lt;br /&gt;  dsResponse.setStartRow(dsRequest.getStartRow());&lt;br /&gt;  dsResponse.setEndRow(dsRequest.getStartRow() + result.size());&lt;br /&gt;  dsResponse.setTotalRows(totalRows);&lt;br /&gt;  return dsResponse;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private String addCriteria(String queryString, Map&amp;ltString, Object&amp;gt; criteria, Map&amp;lt;String, Object&amp;gt; parameters) {&lt;br /&gt;  String separator = " where ";&lt;br /&gt;  for(String paramName : criteria.keySet()) {&lt;br /&gt;   Object paramValue = criteria.get(paramName);&lt;br /&gt;   queryString += separator + addCriterium(paramName, paramValue, parameters);&lt;br /&gt;   separator = " and ";&lt;br /&gt;  }&lt;br /&gt;  return queryString;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /** Override this method for special handling of query parameters. */&lt;br /&gt; protected String addCriterium(String paramName, Object paramValue, Map&amp;lt;String, Object&amp;gt; parameters) {&lt;br /&gt;  parameters.put(paramName, paramValue);&lt;br /&gt;  return paramName + " = :" + paramName;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private &amp;lt;T&amp;gt; List&amp;lt;T&amp;gt; selectObjects(EntityManager em, DSRequest dsRequest, String queryString, Map&amp;lt;String, Object&amp;gt; parameters) {&lt;br /&gt;  queryString = addOrderBy(queryString, dsRequest.getSortBy());&lt;br /&gt;  Query query = em.createQuery(queryString);&lt;br /&gt;  addPagination(dsRequest, query);&lt;br /&gt;  setQueryParameters(query, parameters);&lt;br /&gt;  return query.getResultList();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private String addOrderBy(String queryString, String sortBy) {&lt;br /&gt;  if(sortBy != null) {&lt;br /&gt;   if(sortBy.startsWith("-")) {&lt;br /&gt;    queryString += " ORDER BY " + sortBy.substring(1) + " DESC";&lt;br /&gt;   } else {&lt;br /&gt;    queryString += " ORDER BY " + sortBy + " ASC";&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return queryString;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private void addPagination(DSRequest dsRequest, Query query) {&lt;br /&gt;  long startRow = dsRequest.getStartRow();&lt;br /&gt;  if(startRow &amp;gt;= 0) {&lt;br /&gt;   query.setFirstResult((int)startRow);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  long endRow = dsRequest.getEndRow();&lt;br /&gt;  if(endRow &amp;gt; startRow) {&lt;br /&gt;   query.setMaxResults((int)endRow - (int)startRow);&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private long countObjects(EntityManager em, DSRequest dsRequest, String queryString, Map&amp;lt;String, Object&amp;gt; parameters, List result) {&lt;br /&gt;  long totalRows;&lt;br /&gt;  long startRow = dsRequest.getStartRow();&lt;br /&gt;  long endRow = dsRequest.getEndRow();&lt;br /&gt;  if(startRow &amp;lt; 0 || endRow &amp;lt;= startRow) {&lt;br /&gt;   totalRows = result.size();&lt;br /&gt;   log.debug("no pagination, totalRows=" + totalRows);&lt;br /&gt;  } else if(result.size() &amp;lt; endRow - startRow) {&lt;br /&gt;   totalRows = result.size();&lt;br /&gt;   log.debug("all rows received, totalRows=" + totalRows);&lt;br /&gt;  } else {&lt;br /&gt;   queryString = "select count(*) " + queryString;&lt;br /&gt;   log.debug("countObjects: queryString=" + queryString + ", parameters=" + parameters);&lt;br /&gt;   Query countQuery = em.createQuery(queryString);&lt;br /&gt;   setQueryParameters(countQuery, parameters);&lt;br /&gt;   Object countResult = countQuery.getSingleResult();&lt;br /&gt;   log.debug("countObjects: result=" + countResult);&lt;br /&gt;   totalRows = (Long) countResult;&lt;br /&gt;  }&lt;br /&gt;  return totalRows;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private void setQueryParameters(Query query, Map&amp;lt;String,Object&amp;gt; parameters) {&lt;br /&gt;  for(String paramName : parameters.keySet()) {&lt;br /&gt;   Object paramValue = parameters.get(paramName);&lt;br /&gt;   query.setParameter(paramName, paramValue);&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;import java.util.Map;&lt;br /&gt;&lt;br /&gt;import com.isomorphic.datasource.DSRequest;&lt;br /&gt;import com.isomorphic.datasource.DSResponse;&lt;br /&gt;import com.isomorphic.datasource.DataSource;&lt;br /&gt;&lt;br /&gt;public class AuctionRequestHandler extends RequestHandler {&lt;br /&gt; @Override&lt;br /&gt; protected DSResponse handleInsideTransaction(DSRequest dsRequest) throws Exception {&lt;br /&gt;  if(dsRequest.getOperationType().equals(DataSource.OP_FETCH)) {&lt;br /&gt;   return query(dsRequest, Auction.class);&lt;br /&gt;  } else {&lt;br /&gt;   return dsRequest.execute();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; @Override&lt;br /&gt; protected String addCriterium(String paramName, Object paramValue, Map&amp;lt;String, Object&amp;gt; parameters) {&lt;br /&gt;  if(paramName.equals("maxPrice")) {&lt;br /&gt;   parameters.put(paramName, (Float) paramValue);&lt;br /&gt;   return "startingPrice &amp;lt;= :" + paramName;&lt;br /&gt;  } else if(paramName.equals("title")) {&lt;br /&gt;   return "UPPER(" + paramName + ") LIKE '%" + ((String)paramValue).toUpperCase() + "%'";&lt;br /&gt;  } else {&lt;br /&gt;   return super.addCriterium(paramName, paramValue, parameters);&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Implement user interface&lt;/h2&gt;When the server logic works, it is relatively straight forward to implement the user interface. The code looks something like this: &lt;br /&gt;&lt;pre&gt;&lt;code&gt;    public void onModuleLoad() {&lt;br /&gt;        final DataSource auctionDS = DataSource.get("Auction");&lt;br /&gt;        final DataSource bidDS = DataSource.get("Bid");&lt;br /&gt;&lt;br /&gt;        final ListGrid auctionGrid = new ListGrid();&lt;br /&gt;        auctionGrid.setDataSource(auctionDS);&lt;br /&gt;        auctionGrid.setAutoFetchData(true);&lt;br /&gt;&lt;br /&gt;        final ListGrid bidGrid = new ListGrid();&lt;br /&gt;        bidGrid.setDataSource(bidDS);&lt;br /&gt;        bidGrid.setAutoFetchData(false);&lt;br /&gt;        ...&lt;br /&gt;    }&lt;/code&gt;&lt;/pre&gt;That's all SmartGWT needs to fetch data from the server when needed.  &lt;br /&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;Is it really worth all this effort? I would say yes. It is a lot less work than it would be to implement your own server logic, and in many cases you can just use the default SmartGWT data sources. If you like SQL better than JPA, you can use SmartGWT SQL datasources, wich are more flexible than the JPA, but then you cannot use this unit test framework.&lt;br /&gt;&lt;br /&gt;This testing methodology has saved us lots of work. It is far cheaper to test the logic in unit tests than to test it manually, fix problems, and restart the application to test again. With this kind of unit tests, most of the server logic just works. The remaining work is to implement and fine-tune the user interface. And of course, the unit tests keep our code working.&lt;br /&gt;&lt;br /&gt;It is actually not just a testing methodology but a requirements methodology also. Using examples and objects makes the communication between clients/analysts and developers crystal clear. It reveals misunderstanding and missing details so that the developers get all the information they need to test and implement new functionality.&lt;br /&gt;&lt;br /&gt;Object cases make it easy to design tests, and the tests makes it easy to focus development and figure out what to do next.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-3421642571379817508?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/3421642571379817508/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=3421642571379817508' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/3421642571379817508'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/3421642571379817508'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2011/06/test-driven-development-with-smartgwt.html' title='Test driven development with SmartGWT and JPA'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-mr-OE3xOpDo/TgCtP0pWTHI/AAAAAAAAAF8/KvjS8ghT6-E/s72-c/Create%2Bauction.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-9009629973871212269</id><published>2011-06-02T23:17:00.001+02:00</published><updated>2011-06-03T01:06:16.690+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='communication'/><title type='text'>Back to the agile values</title><content type='html'>In recent years the term agile has become overused. Many seem to think that if they have have unit tests, standup meetings and burn-down charts, they're agile. All these practices are good, but they don't necessarily make you agile. Even iterations or some kind of certified master don't necessarily make you agile.&lt;br /&gt;&lt;br /&gt;So what is agile? If I were to sum it up with one word it would be &lt;i&gt;communication&lt;/i&gt;. Communication is everywhere in the &lt;a href="http://www.agilemanifesto.org"&gt;agile manifesto&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;b class="bold"&gt;[We value] individuals and interactions over processes and tools.&lt;/b&gt;&lt;br /&gt;I see this as a reaction against processes like RUP that felt like a software development factory where developers were replacable cog wheels. Agile recognizes that it's individuals with intelligence, creativity and drive that make a project succeed.&lt;br /&gt;&lt;br /&gt;But individuals are not working in isolation, they need to interact with others. Interactions means communication. Not one-way communication but interactive dialogues. Misunderstandings are inevitable in communication. When you say or write something, it is almost certain that the receiver will misunderstand something. You can't just send someone a document and think they will understand what you mean. You need to verify what they understood, and the best way to do this is in a face to face conversation.&lt;br /&gt;&lt;br /&gt;A key part of agile is to have close communication within the team and between the team and the customers.&lt;br /&gt;&lt;br /&gt;&lt;b class="bold"&gt;[We value] working software over comprehensive documentation.&lt;/b&gt;&lt;br /&gt;Documentation is a form of communication. Some teams stop writing documents "because that's not agile", but that's a huge misunderstanding. Agile does value documentation, but it values working software more. Working software demonstrates progress better than completing a number of documents, and it demonstrates the team's understanding of the requirements better than a requirements document. But documentation may be useful to explain what you were thinking when you developed the software.&lt;br /&gt;&lt;br /&gt;I said that iterations don't necessarily make you agile, but iterations are definitely needed to be agile. Iterations is not a goal in itself, the purpose of iterations is to improve communication with the customers by getting feedback often. It is inevitable that we misunderstand what the customers need. Iterations help us to discover these misunderstandings early, before they get too expensive to fix.&lt;br /&gt;&lt;br /&gt;&lt;b class="bold"&gt;[We value] customer collaboration over contract negotiation.&lt;/b&gt;&lt;br /&gt;Collaboration certainly means communication. The development team needs a positive dialogue with the customers, and not just communicate with formal documents.&lt;br /&gt;&lt;br /&gt;&lt;b class="bold"&gt;[We value] responding to change over following a plan.&lt;/b&gt;&lt;br /&gt;This may not seem like to be about communication, but actually it is. Where do the changes come from? From the customers. The  customers and the team should communicate often, not just up front.&lt;br /&gt;&lt;br /&gt;These values are the basis for practices like on-site customer, iterative development and pair programming. It's the values that make you agile, not various practices. The practices vary depending on the size and complexity of the project.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-9009629973871212269?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/9009629973871212269/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=9009629973871212269' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/9009629973871212269'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/9009629973871212269'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2011/06/back-to-agile-values.html' title='Back to the agile values'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-3679894721661577382</id><published>2011-05-16T10:03:00.000+02:00</published><updated>2011-05-16T10:03:28.485+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='requirements'/><category scheme='http://www.blogger.com/atom/ns#' term='Use cases'/><category scheme='http://www.blogger.com/atom/ns#' term='object cases'/><category scheme='http://www.blogger.com/atom/ns#' term='examples'/><title type='text'>The problem with use cases</title><content type='html'>The greatest benefit I get from use cases is that they focus on the user. Use cases help me to think about what the user wants to do instead of only focusing on implementation details.&lt;br /&gt;&lt;br /&gt;The biggest problem I have with use cases is that they are not structured. They are basically free text. For instance, if we have a use case Withdraw money from ATM, we may define that it has a precondition that Open account is performed, but we don't get any help from the method to see that.&lt;br /&gt;&lt;br /&gt;What happens if someone later changes the Open account use case or defines a Close account use case? How do we find which other uses cases that need to be modified? We can look through the old use case diagrams and find dependencies, but I can almost guarrantee that these dependencies have not been maintained after they were initially created.&lt;br /&gt;&lt;br /&gt;The solution to this is to connect the use cases to an object model. I don't mean a use-case realization with view and controller objects like ATM_Screen and ATM_Session, I mean the model objects from the problem domain, such as Customer, Account and Transaction. These are the terms you use when you discuss the functionality with actual users.&lt;br /&gt;&lt;br /&gt;If you model the Withdraw money from ATM use case as objects, it could be something like this:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;You have an Account object with balance $1000.&lt;/li&gt;&lt;li&gt;You insert a card in an ATM and enters the pin code 1234.&lt;/li&gt;&lt;li&gt;You withdraw $100 from the ATM.&lt;/li&gt;&lt;li&gt;Afterwards, the Account balance is changed to $900 and a new Transaction is created.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-gJRjnoWqSso/Tb51RVv42gI/AAAAAAAAAFg/6ORQ9yGpazU/s1600/account-and-transaction.png" imageanchor="1" style=""&gt;&lt;img border="0" height="224" width="400" src="http://2.bp.blogspot.com/-gJRjnoWqSso/Tb51RVv42gI/AAAAAAAAAFg/6ORQ9yGpazU/s400/account-and-transaction.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;To find dependencies between this use case and other use cases, we ask where the Account object and balance came from. This is something to discuss with the client or end users. They may answer that you can open an account and deposit cash.&lt;br /&gt;&lt;br /&gt;The Open account use case can be modeled as follows:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The customer gives name and address and shows id to a teller.&lt;/li&gt;&lt;li&gt;Create an Account object.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;The Deposit cash use case can be modeled as follows:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;You have an Account with balance $0.&lt;/li&gt;&lt;li&gt;The customer gives the account number 12345678 and $1000 in cash to a teller.&lt;/li&gt;&lt;li&gt;The Account balance is updated to $1000.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;div class="bold"&gt;There you have the dependencies between Open account, Deposit cash and Withdraw money from ATM: They all use Account objects.&lt;/div&gt;&lt;br /&gt;&lt;h2&gt;Analyze objects to find more functionality&lt;/h2&gt;We can continue with our analysis by asking questions such as:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Can the balance be changed in any other way than Deposit cash and Withdraw money from ATM?&lt;/li&gt;&lt;li&gt;Where did the Account number come from? Can it be changed?&lt;/li&gt;&lt;li&gt;How is the pin code set?&lt;/li&gt;&lt;li&gt;Can Accounts be deleted?&lt;/li&gt;&lt;li&gt;In which ways can you read Account numbers, balances and pin codes?&lt;/li&gt;&lt;li&gt;How can Transactions be created, updated, read and deleted?&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;The questions above deal with creation, deletion, update and reading of domain objects. They will help to discover more functionality, which can be analyzed further, and so on, until you have a very complete functional specification.&lt;br /&gt;&lt;br /&gt;There are a few more questions that can be helpful to discover more functionality. They are based on 4 kinds of classes: People, places, things and events.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Ask which people will perform the various functionality. This will help us to model users, roles and privileges.&lt;/li&gt;&lt;li&gt;Finally, ask which information to store about user actions (events). Transaction is such a class that stores information about changes of Account balance.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;This method is a fast way to come up with lots of requirements which would otherwise take days or weeks of brainstorming. It is similar to use cases in that focuses on what the users can do. But it is different in that it models this as objects. Therefore, I am calling this method object cases.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;What exactly is an object case?&lt;/h2&gt;An object case is a use case scenario connected to domain objects. It consists of 4 parts:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Initial objects&lt;/li&gt;&lt;li&gt;Input&lt;/li&gt;&lt;li&gt;Output&lt;/li&gt;&lt;li&gt;Result objects&lt;/li&gt;&lt;/ol&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-3XUvcybr9lA/Tcf3lyiP-pI/AAAAAAAAAFo/khvpfMwmsUM/s1600/input-output-objects.png" imageanchor="1" style=""&gt;&lt;img border="0" height="145" width="302" src="http://4.bp.blogspot.com/-3XUvcybr9lA/Tcf3lyiP-pI/AAAAAAAAAFo/khvpfMwmsUM/s400/input-output-objects.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The input, output and objects are specified with concrete values. In my experience, this improves communication with clients and end users, because it reduces misunderstandings and help to find missing details.&lt;br /&gt;&lt;br /&gt;Object cases are also a great help for developers; they have all the information they need in the object case and don't need to ask for clarifications so much. &lt;br /&gt;&lt;br /&gt;Testers can also benefit from object cases. Instead of writing test specificiations based on use cases or some other abstract requirements, the object cases are ready to test.&lt;br /&gt;&lt;br /&gt;Objects can also be used for project planning: Develop the functionality around a domain class together. Start with the function that creates objects of that class.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-3679894721661577382?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/3679894721661577382/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=3679894721661577382' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/3679894721661577382'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/3679894721661577382'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2011/05/problem-with-use-cases.html' title='The problem with use cases'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-gJRjnoWqSso/Tb51RVv42gI/AAAAAAAAAFg/6ORQ9yGpazU/s72-c/account-and-transaction.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-6944265732169558980</id><published>2011-05-10T16:52:00.001+02:00</published><updated>2011-05-10T17:00:32.477+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='object cases'/><category scheme='http://www.blogger.com/atom/ns#' term='examples'/><category scheme='http://www.blogger.com/atom/ns#' term='communication'/><title type='text'>Getting the requirements right with object cases</title><content type='html'>One of the biggest problems in software projects is poor requirements. We always seem to misunderstand what the user is really trying to do or discover some missing functionality at the end of the project. Agile methods reduce this problem with short feedback cycles to discover misunderstandings early, but it would be even better to get rid of the misunderstandings before implementation.&lt;br /&gt;&lt;br /&gt;Two things that really help me in this area are examples and objects. This article describes how to combine these with a method I am calling object cases.&lt;br /&gt;&lt;br /&gt;An object case is an example of a user function connected to domain objects. It consists of 4 parts:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Initial objects&lt;/li&gt;&lt;li&gt;Input&lt;/li&gt;&lt;li&gt;Output&lt;/li&gt;&lt;li&gt;Result objects&lt;/li&gt;&lt;/ol&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-3XUvcybr9lA/Tcf3lyiP-pI/AAAAAAAAAFo/khvpfMwmsUM/s1600/input-output-objects.png" imageanchor="1" style=""&gt;&lt;img border="0" height="145" width="302" src="http://4.bp.blogspot.com/-3XUvcybr9lA/Tcf3lyiP-pI/AAAAAAAAAFo/khvpfMwmsUM/s400/input-output-objects.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;A typical week using object cases can be as follows:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Monday morning: Go through new functionality with the client.&lt;/li&gt;&lt;li&gt;Monday afternoon: Planning and estimation.&lt;/li&gt;&lt;li&gt;Tuesday: Implement automatic tests.&lt;/li&gt;&lt;li&gt;Wednesday - Friday: Implement the actual functionality.&lt;/li&gt;&lt;/ol&gt;My experience is that object cases help us a lot in all this activities. When discussing requirements, the objects generate questions such as "How do I find this object?" or "When is this object created?" These kinds of questions helps us to discover missing functionality early and saves us lots of rework.&lt;br /&gt;&lt;br /&gt;A consequence of this is that the plans are really good. It doesn't happen often that we forget tasks in the plan.&lt;br /&gt;&lt;br /&gt;Another experience is that after Monday, I hardly need to discuss anything with the client. The object cases give me all the details I need to implement automatic tests and production code without guessing.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Examples&lt;/h2&gt;I always use some examples to make things concrete. This helps to clear out the inevitable misunderstandings and reveal details we did not think of.&lt;br /&gt;&lt;br /&gt;By example, I mean a concrete example of something a user can do. For instance, here is an example of how to use a system for enrollment in university seminars:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #e6e6e6; color:black; border-style:solid; border-width:1px; padding-left:1em; padding-top:0.5em; "&gt;&lt;b&gt;Jane Enrolls in Seminar&lt;/b&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Jane enters her name: “Jane Jackson” and student number: "111-222".&lt;/li&gt;&lt;li&gt;System displays the available seminars: ENG 103, COM 402.&lt;/li&gt;&lt;li&gt;Jane chooses seminar ENG 103.&lt;/li&gt;&lt;li&gt;System calculates and displays fees: $800&lt;/li&gt;&lt;li&gt;Jane indicates she wants to enroll.&lt;/li&gt;&lt;li&gt;System enrolls Jane in the seminar.&lt;/li&gt;&lt;li&gt;System sends a bill to Jane Jackson with the following content: ENG 103, fall 2011, $800&lt;/li&gt;&lt;li&gt;System prints an enrollment receipt: Jane Jackson, ENG 103, fall 2011.&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;br /&gt;Note that the example has a concrete user and concrete input and output values everywhere.&lt;br /&gt;&lt;br /&gt;I have never studied in America, so I am sure there are some mistakes and missing details. But that is one of the main points of using concrete examples: &lt;b class="bold"&gt;It is easy to spot mistakes.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The alternative is an abstract requirement, such as a use case:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #e6e6e6; color:black; border-style:solid; border-width:1px; padding-left:1em; padding-top:0.5em; "&gt;&lt;b&gt;Enroll in Seminar&lt;/b&gt;&lt;br /&gt;Basic Course of Action:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Student inputs her name and student number.&lt;/li&gt;&lt;li&gt;System verifies the student is eligible to enroll in seminars. If not eligible then the student is informed and use case ends.&lt;/li&gt;&lt;li&gt;System displays list of available seminars.&lt;/li&gt;&lt;li&gt;Student chooses a seminar or decides not to enroll at all.&lt;/li&gt;&lt;li&gt;System validates the student is eligible to enroll in the chosen seminar. If not eligible, the student is asked to choose another.&lt;/li&gt;&lt;li&gt;System validates the seminar fits into the student’s schedule.&lt;/li&gt;&lt;li&gt;System calculates and displays fees.&lt;/li&gt;&lt;li&gt;Student verifies the cost and either indicates she wants to enroll or not.&lt;/li&gt;&lt;li&gt;System enrolls the student in the seminar and bills them for it.&lt;/li&gt;&lt;li&gt;The system prints enrollment receipt.&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;br /&gt;(Source: &lt;a href="http://www.agilemodeling.com/essays/agileRequirements.htm"&gt;Agile Requirement Modeling&lt;/a&gt; by Scott W. Ambler.)&lt;br /&gt;&lt;br /&gt;The use case above is actually very good, but the example has more information:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A student number is in the format 111-222.&lt;/li&gt;&lt;li&gt;The University has seminars that are called for instance ENG 103 and COM 402.&lt;/li&gt;&lt;li&gt;A typical fee for a seminar can be $800.&lt;/li&gt;&lt;li&gt;The bill includes information about the student name, seminars, semester and total price.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;When I discuss the example with the stakeholders, they are likely to find errors in my assumptions and point them out. &lt;b class="bold"&gt;This gives me better quality of the requirements before I start development.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;More importantly, examples enable further discussions with the customer, such as:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Which information should be displayed in the list of seminars?&lt;/li&gt;&lt;li&gt;Which seminars shall be listed?&lt;/li&gt;This may for instance be: "All seminars with seats left that the student is eligible to." An obvious follow-up question would be what it means to be eligible. Be sure to ask for some examples.&lt;li&gt;How is the fee of $800 calculated?&lt;/li&gt;The answer may be something like: "A fee of $100 per term and $700 for ENG 103." &lt;/ul&gt;&lt;br /&gt;The answers to these questions will likely lead to requirements for more functionality that the system needs to support. For instance, functionality is needed to edit course fees.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Objects&lt;/h2&gt;The next step is to model these examples as objects.&lt;br /&gt;&lt;br /&gt;In the example above, we need the following initial objects:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-yWbj9MUhuGc/TblT5t6-EoI/AAAAAAAAAFI/nHWPDrhlpYw/s1600/objects1.png" imageanchor="1" style=""&gt;&lt;img border="0" height="182" width="400" src="http://4.bp.blogspot.com/-yWbj9MUhuGc/TblT5t6-EoI/AAAAAAAAAFI/nHWPDrhlpYw/s400/objects1.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;In the resulting objects, a Bill object should be created and a Seminar object updated:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-5ZlSLLKLgB8/TblT_0pnlVI/AAAAAAAAAFQ/Nw3PziVI0uQ/s1600/objects2.png" imageanchor="1" style=""&gt;&lt;img border="0" height="182" width="400" src="http://2.bp.blogspot.com/-5ZlSLLKLgB8/TblT_0pnlVI/AAAAAAAAAFQ/Nw3PziVI0uQ/s400/objects2.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The objects give structure to the examples. This makes it easy to find missing pieces in the requirements. They inspire more questions, such as when is the Student object created, what happens when the Bill is paid or not paid?&lt;br /&gt;&lt;br /&gt;Just using examples and objects informally will greatly improve the communication between stakeholders and programmers. The stakeholders understand the examples and the programmers get all the information they need. Another benefit is that the examples can be used as functional tests.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Completeness&lt;/h2&gt;The examples can be used to find objects, which can be used to find more examples, and so on. In this way, we can build a complete specification.&lt;br /&gt;&lt;br /&gt;To ensure that the model is complete, we ask these questions:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Can all output be calculated from the objects?&lt;/li&gt;&lt;li&gt;Is all input used to find or modify objects?&lt;/li&gt;&lt;/ol&gt;In the example above, this becomes:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #e6e6e6; color:black; border-style:solid; border-width:1px; padding-left:1em; padding-top:0.5em; "&gt;&lt;b&gt;Jane Enrolls in Seminar&lt;/b&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Input name: “Jane Jackson” and student number: "111-222". &lt;i&gt;(Used to find a Student object.)&lt;/i&gt;&lt;/li&gt;&lt;li&gt;Output available seminars: ENG 103, COM 402. &lt;i&gt;(Calculated from the Seminar objects.)&lt;/i&gt;&lt;/li&gt;&lt;li&gt;Input seminar ENG 103. &lt;i&gt;(Used to find a Seminar object.)&lt;/i&gt;&lt;/li&gt;&lt;li&gt;Output fees: $800. &lt;i&gt;(Calculated from the Bill object.)&lt;/i&gt;&lt;/li&gt;&lt;li&gt;-&lt;/li&gt;&lt;li&gt;-&lt;/li&gt;&lt;li&gt;Output bill: ENG 103, fall 2011, $800.&lt;div style="color:red;"&gt;&lt;i&gt;&amp;nbsp;(How is 'fall 2011' calculated? We need to add this to the object model, perhaps in a new Term object.)&lt;/i&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;Output enrollment receipt: Jane Jackson, ENG 103, fall 2011.&lt;i&gt;&amp;nbsp;(Same as above.)&lt;/i&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;br /&gt;This results in the following class model:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-m3y9_Szh_Us/TblRUfMFXoI/AAAAAAAAAE4/v1QbZV7leCw/s1600/classes.png" imageanchor="1" style=""&gt;&lt;img border="0" height="165" width="400" src="http://2.bp.blogspot.com/-m3y9_Szh_Us/TblRUfMFXoI/AAAAAAAAAE4/v1QbZV7leCw/s400/classes.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;We can use the class model to find more examples. We need examples that create, delete, update and read all the classes.&lt;br /&gt;&lt;br /&gt;Our example creates a Bill object. But when are Student, Seminar and Term objects created? When are these objects updated and can they be deleted?&lt;br /&gt;&lt;br /&gt;If we ask the client, they will tell us more functionality that the system needs to have, for instance:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Register student&lt;/li&gt;&lt;li&gt;Plan seminar&lt;/li&gt;&lt;li&gt;(and some more examples to update and delete objects)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Finally, we can ask the client for examples of how this information is used (read). Our example reads information from Student and Seminar objects. But when are the Bill objects read? Again, this needs to be discussed with the client. Instead of just creating a simplistic user interface with CRUD operations that displays tables of seminars, students etc, we should find out what the users really want to do. This will help us to design a good user interface.&lt;br /&gt;&lt;br /&gt;In this case, the client might want the following functionality:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Print a list of all unpaid bills.&lt;/li&gt;&lt;li&gt;Print a list of all students in a seminar.&lt;/li&gt;&lt;li&gt;Send email to all students in a seminar.&lt;/li&gt;&lt;li&gt;Calculate the total seminar fees for a term.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;When we make concrete examples with input and output for all this, we will probably need even more objects, and so the requirements grow. &lt;b class="bold"&gt;Connecting objects and examples is a great way to discover missing functionalilty.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;How does this help?&lt;/h2&gt;Examples improve the communication. They give clients, analysts, developers and testers a common understanding of what the system needs to do. They are sufficiently detailed so people don't have to guess.&lt;br /&gt;&lt;br /&gt;Objects give structure to the examples. This helps us to make the examples consistent and complete.&lt;br /&gt;&lt;br /&gt;Objects and examples is a great starting point for developers. Firstly, they have very detailed requirements. They don't need to guess or ask the client all the time. Secondly, the developers can use the object model as a basis for implementation. And thirdly, they can write automatic tests from the examples.&lt;br /&gt;&lt;br /&gt;The examples are testable, so the testers can basically test the examples, but they will need to write more examples than the "sunny day" scenarios that clients and developers come up with. It is a good idea to involve the testers in defining the examples to discuss these details early.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-6944265732169558980?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/6944265732169558980/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=6944265732169558980' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/6944265732169558980'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/6944265732169558980'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2011/05/getting-requirements-right-with-object.html' title='Getting the requirements right with object cases'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-3XUvcybr9lA/Tcf3lyiP-pI/AAAAAAAAAFo/khvpfMwmsUM/s72-c/input-output-objects.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-1484510964854307515</id><published>2011-02-18T13:41:00.001+01:00</published><updated>2011-02-18T13:41:34.649+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='Mercurial'/><title type='text'>Character encoding with MercurialEclipse</title><content type='html'>I'm posting this here since it took me some time to find it.&lt;br /&gt;&lt;br /&gt;In Eclipse with Mercurial and MercurialEclipse, I have some files with UTF-8 encoding. When I compared these files, I would see differences at every line with an international character.&lt;br /&gt;&lt;br /&gt;Solution: Change the project encoding to UTF-8 (Project Properties &gt; Resource) and restart Eclipse.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-1484510964854307515?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/1484510964854307515/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=1484510964854307515' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/1484510964854307515'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/1484510964854307515'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2011/02/character-encoding-with.html' title='Character encoding with MercurialEclipse'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-6508411209615816560</id><published>2011-02-09T16:56:00.023+01:00</published><updated>2011-02-10T22:32:55.682+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQLite'/><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><category scheme='http://www.blogger.com/atom/ns#' term='JPA'/><category scheme='http://www.blogger.com/atom/ns#' term='reverse engineer'/><category scheme='http://www.blogger.com/atom/ns#' term='DB Importer'/><title type='text'>Reading FireFox bookmarks with JPA</title><content type='html'>One of the primary goals of DB Importer is simplicity. To make this concrete, the goal is to be able to start using an existing database with JPA in 5 minutes.&lt;br /&gt;&lt;br /&gt;Now it is time to test this. I choose the FireFox SQLite bookmarks database as an example. Not because it is easy, but because it is hard ;-)&lt;br /&gt;&lt;br /&gt;Preconditions: Eclipse and &lt;a href="http://objectgeneration.com/importdb.html"&gt;DB Importer&lt;/a&gt; are installed and a Java project is created.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. Reverse Engineer Database&lt;/b&gt;&lt;br /&gt;Right-click on a Java package and select DB Importer &gt; Import from Database.&lt;br /&gt;Select the org.sqlite.JDBC driver in the wizard:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://1.bp.blogspot.com/-26wf_bsxWfo/TVOuyNO3mGI/AAAAAAAAADI/BLR6nhlX99E/s1600/import-sqlite-wizard1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5571989341556152418" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. Locate the Database&lt;/b&gt;&lt;br /&gt;I need to replace &amp;lt;sqlite-file&amp;gt; in the URL with the actual database file. But where does FireFox store its bookmarks?&lt;br /&gt;&lt;br /&gt;To find out this, I turn to my favourite programming tool: Google. Google "firefox bookmarks database" and click on the top link gives me &lt;a href="http://www.lytebyte.com/2008/06/19/understanding-how-and-where-firefox-3-bookmarks-are-saved/"&gt;this site&lt;/a&gt;, which explains that you should look for a file called "places.sqlite" under C:\Users\&amp;lt;user name&amp;gt;\AppData\Roaming\Mozilla\Firefox\Profiles\&amp;lt;some random characters&amp;gt;.default.&lt;br /&gt;&lt;br /&gt;To find this file on my Mac, I open a terminal and enter:&lt;br /&gt;&lt;pre&gt;ls $HOME/Library/Application\ Support/Firefox/Profiles/*/places.sqlite&lt;/pre&gt;This gives me the file, and i enter the full path in the wizard:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://3.bp.blogspot.com/-YlTXtbdXxzA/TVOvVkrzUTI/AAAAAAAAADQ/uHFiyL6hMYk/s1600/import-sqlite-wizard2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5571989949146943794" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. Generate Entity Classes&lt;/b&gt;&lt;br /&gt;When I push Next, a new editor is opened:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://4.bp.blogspot.com/-MxvesGjIk98/TVO05r9c4vI/AAAAAAAAAD4/OM3V0IOHGLc/s1600/import-sqlite-editor.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5571996067133448946" /&gt;&lt;br /&gt;&lt;br /&gt;This shows the tables and columns in the database and a preview of the Java code that will be generated.&lt;br /&gt;&lt;br /&gt;Two of the tables are marked with errors. Clicking on them reveals that this is because they lack a primary key, so they cannot be used with JPA.&lt;br /&gt;&lt;br /&gt;I Save the editor. This generates the Java files, but they have compilation errors because I have not yet added the JPA libraries.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. Create a Test Application&lt;/b&gt;&lt;br /&gt;To use JPA, I need to create a configuration file (persistence.xml) and add the JPA libraries and the JDBC driver to the project. This can be done with Maven, or you can use a wizard in DB Importer to quickly create a test application: Right-click on the Java package and select DB Importer &gt; New Test Application. This opens another wizard:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://2.bp.blogspot.com/-FVT7uSO1mBg/TVOvrd4nFrI/AAAAAAAAADY/jUVGO5CLaes/s1600/import-sqlite-app.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5571990325278742194" /&gt;&lt;br /&gt;&lt;br /&gt;I enter a class name and push Finish. This adds all the necessary JAR files to the project, creates SQLiteDialect.java, persistence.xml and PrintBookmarks.java and opens the two last files.&lt;br /&gt;&lt;br /&gt;The generated PrintBookmarks class is just a template:&lt;br /&gt;&lt;pre&gt;public class PrintBookmarks {&lt;br /&gt;public static void main(String[] args) {&lt;br /&gt; javax.persistence.EntityManagerFactory factory =&lt;br /&gt;    javax.persistence.Persistence.createEntityManagerFactory("unit1");&lt;br /&gt; javax.persistence.EntityManager em = factory.createEntityManager();&lt;br /&gt; javax.persistence.EntityTransaction tx = em.getTransaction();&lt;br /&gt; tx.begin();&lt;br /&gt; try {&lt;br /&gt;    // TODO Replace User with a real @Entity class.&lt;br /&gt;    java.util.List&lt;user&gt; result = em.createQuery("from User").&lt;br /&gt;      getResultList();&lt;br /&gt;    System.out.println("Found " + result.size() + " objects:");&lt;br /&gt;    for(User obj : result) {&lt;br /&gt;       // TODO Change to some real method.&lt;br /&gt;       System.out.println(obj.getName());&lt;br /&gt;    }&lt;br /&gt;    tx.commit();&lt;br /&gt; } catch(RuntimeException e) {&lt;br /&gt;    tx.rollback();&lt;br /&gt;    throw e;&lt;br /&gt; } finally {&lt;br /&gt;    em.close();&lt;br /&gt;    factory.close();&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;}&lt;/user&gt;&lt;/pre&gt;&lt;br /&gt;But just by replacing "User" with "MozBookmarks" and "obj.getName()" with "obj.getTitle()" we have a working application.&lt;br /&gt;&lt;br /&gt;Let's try: Run As &gt; Java Application.&lt;br /&gt;&lt;br /&gt;Success! All my bookmarks are printed. And it went really quickly, so I think I am satisfied with the simplicity.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Bonus: Modify the Generated Code&lt;/b&gt;&lt;br /&gt;Simplicity is one thing, but how about flexibility? DB Importer gives you all the flexibility you need with a Groovy script that configures the generated code. Don't worry if you don't know Groovy, the script uses Java syntax.&lt;br /&gt;&lt;br /&gt;(Note that configuring this script is only available in the commercial edition of DB Importer. You get simplicity for free but flexibility is commercial.)&lt;br /&gt;&lt;br /&gt;First, I want to change the generated class names. For instance, I want the table MOZ_BOOKMARKS to be reverse engineered to a Java class Bookmark.&lt;br /&gt;&lt;br /&gt;I select a table in the tree and double-click on the className in the table to the right. This opens an editor with ImportDB.groovy. This script allows you to modify anything you want in the generated Java code. I modify the className method and Save the editor. This changes the className in the preview from MozBookmarks to Bookmark.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://4.bp.blogspot.com/-VJyFaQjLSCs/TVO4Dw6LOJI/AAAAAAAAAEA/O4FMKqo_mYc/s1600/import-sqlite-classname.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5571999538795460754" /&gt;&lt;br /&gt;&lt;br /&gt;I would also like to generate relations for foreign keys. The referenced table is missing in SQLite, so I need to calculate the referenced table based on the column name. I edit the getReferencedTable method as follows and Save the import script.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://1.bp.blogspot.com/-WBzODnJhkLk/TVOz0Xw61tI/AAAAAAAAADw/8AEuivtsSoQ/s1600/import-sqlite-relations.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5571994876301203154" /&gt;&lt;br /&gt;&lt;br /&gt;I can then modify the test app to use the Bookmarks - Place relation. I also modify it to print the bookmark hierarchy:&lt;br /&gt;&lt;pre&gt;/**&lt;br /&gt; * List all FireFox bookmarks.&lt;br /&gt; * This depends on correct configuration in&lt;br /&gt; * persistence.xml. Also, FireFox must not be running,&lt;br /&gt; * since it locks the database.&lt;br /&gt; */&lt;br /&gt;public class PrintBookmarks {&lt;br /&gt;    private EntityManagerFactory factory;&lt;br /&gt;    private EntityManager em;&lt;br /&gt;    private EntityTransaction tx;&lt;br /&gt;&lt;br /&gt;    public static void main(String[] args) {&lt;br /&gt;        new PrintBookmarks().run();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void run() {&lt;br /&gt;        factory = Persistence.createEntityManagerFactory("unit1");&lt;br /&gt;        em = factory.createEntityManager();&lt;br /&gt;        tx = em.getTransaction();&lt;br /&gt;        tx.begin();&lt;br /&gt;        try {&lt;br /&gt;            printBookmarks(getTopBookmarks(), "");&lt;br /&gt;            tx.commit();&lt;br /&gt;        } catch (RuntimeException e) {&lt;br /&gt;            tx.rollback();&lt;br /&gt;            throw e;&lt;br /&gt;        } finally {&lt;br /&gt;            em.close();&lt;br /&gt;            factory.close();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private void printBookmarks(Collection&lt;Bookmark&gt; list, String mrg) {&lt;br /&gt;        for (Bookmark bm : list) {&lt;br /&gt;            System.out.print(mrg + bm.getTitle());&lt;br /&gt;            Place place = bm.getFk();&lt;br /&gt;            if (place != null) {&lt;br /&gt;                System.out.print(" - " + place.getTitle() + ": "&lt;br /&gt;                        + place.getUrl());&lt;br /&gt;            }&lt;br /&gt;            System.out.println();&lt;br /&gt;            printBookmarks(getChildBookmarks(bm), mrg + "   ");&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private List&lt;Bookmark&gt; getTopBookmarks() {&lt;br /&gt;        return getBookmarksByParent("0");&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private List&lt;Bookmark&gt; getChildBookmarks(Bookmark parent) {&lt;br /&gt;        return getBookmarksByParent(parent.getId());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @SuppressWarnings("unchecked")&lt;br /&gt;    private List&lt;Bookmark&gt; getBookmarksByParent(String parent) {&lt;br /&gt;        Query query = em.createQuery(&lt;br /&gt;            "from Bookmark where parent=:parent");&lt;br /&gt;        query.setParameter("parent", parent);&lt;br /&gt;        return query.getResultList();&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;The complete source with the reverse engineered classes and this test app is available at &lt;a href="https://bitbucket.org/larsho/dbimporter/src/631067cd9413/examples/firefox-bookmarks/"&gt;bitbucket&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-6508411209615816560?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/6508411209615816560/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=6508411209615816560' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/6508411209615816560'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/6508411209615816560'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2011/02/reading-firefox-bookmarks-with-jpa_09.html' title='Reading FireFox bookmarks with JPA'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-26wf_bsxWfo/TVOuyNO3mGI/AAAAAAAAADI/BLR6nhlX99E/s72-c/import-sqlite-wizard1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-5970285026700921991</id><published>2011-01-30T22:05:00.007+01:00</published><updated>2011-02-10T22:33:13.480+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DB Importer'/><title type='text'>Just one more feature...</title><content type='html'>&lt;a href="http://objectgeneration.com/importdb.html"&gt;DB Importer&lt;/a&gt; is ready to launch, but I just want to add one more feature, so I have decided to extend the beta period 2 weeks. I know I should realease early and all that, but key feature of this product is simplicity, and I think I can really make it simpler than it is now.&lt;br /&gt;&lt;br /&gt;My goal is that someone with little or no experience with JPA can create a small Java application that connects  to an existing database using JPA in 5 minutes. To reach this goal, I want to bundle a JPA implementation with the product so you can get started without downloading and integrating those JAR files.&lt;br /&gt;&lt;br /&gt;If you don't care about simplicity, there are other free tools out there. DB Importer is for those who would rather pay a little than using their precious time figuring out how to use a new tool.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-5970285026700921991?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/5970285026700921991/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=5970285026700921991' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/5970285026700921991'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/5970285026700921991'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2011/01/just-one-more-feature.html' title='Just one more feature...'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-3064995312970779664</id><published>2010-12-03T13:52:00.006+01:00</published><updated>2011-02-10T22:33:39.173+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='database'/><category scheme='http://www.blogger.com/atom/ns#' term='JPA'/><category scheme='http://www.blogger.com/atom/ns#' term='reverse engineer'/><category scheme='http://www.blogger.com/atom/ns#' term='DB Importer'/><title type='text'>DB Importer</title><content type='html'>I often need to use an existing database from Java. I like JPA, so I usually want to generate JPA @Entity classes for the existing tables. There are some tools out there that does this, but they are not exactly easy to use. I tried several, and I could not get them to work in a reasonable time. (I have more important things to do than to fight a complicated tool.)&lt;br /&gt;&lt;br /&gt;I also want to configure the import, for instance to generate @Version and @GeneratedValue annotations, to tweak the generated class names, to generate @ManyToMany relations from connection tables, etc.&lt;br /&gt;&lt;br /&gt;So, I decided to write a tool myself. (Why spend a day getting a tool to work when you can make your own in a few weeks?)&lt;br /&gt;&lt;br /&gt;I have already made one tool like this: HiberObjects Import DB Pro, but I realize that most programmers don't want to use UML. So, I made a similar tool but without UML.&lt;br /&gt;&lt;br /&gt;The code generation can be configured with a Groovy script. To keep this simple, I use plain Java syntax in the script.&lt;br /&gt;&lt;br /&gt;The tool is an Eclipse plug-in called &lt;a href="http://objectgeneration.com/importdb.html"&gt;DB Importer&lt;/a&gt;. It will be available in a free edition and a commercial edition. DB Importer is currently in Beta. Please try it out and let me know what you think.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-3064995312970779664?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/3064995312970779664/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=3064995312970779664' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/3064995312970779664'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/3064995312970779664'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2010/12/db-importer.html' title='DB Importer'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-4129028437626971973</id><published>2010-07-20T14:27:00.010+02:00</published><updated>2011-02-10T22:34:05.899+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Make it or break it</title><content type='html'>Some times I am working on a project alone doing both design, planning, programming and testing. I have found it useful to separate these different kinds of work as much as possible. That's because programmers are not good testers. Programming and testing requires totally opposite mindsets:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Success as a programmer is to get something to work &lt;span style="font-style: italic;"&gt;(make it)&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Success as a tester is to find something that doesn't work &lt;span style="font-style: italic;"&gt;(break it)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;If I start testing while I'm in programming mode, I will not try very hard to break it, so lots of errors will slip through.&lt;br /&gt;&lt;br /&gt;But on the other hand, I can easily get into programming from testing. When I am testing, I am working with an example, and the example helps me to focus my programming. But I try to avoid jumping from programming to testing anyway, because it is hard to get back to effective testing again.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1. Design&lt;/span&gt;&lt;br /&gt;I get the best results if I start with designing a few examples of how the functionality will be used. The examples help me to focus and remember all details. I will also use them as my test specification. Then I create automatic tests based on these examples.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2. Implementation&lt;/span&gt;&lt;br /&gt;The automatic tests will remind me what I need to do, so it's no problem to take lunch or end the work day now. But often I am so focused that I just continue with implementation until all the automatic tests pass. After this I take lunch or a long break before I start manual testing.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;3. Manual testing&lt;/span&gt;&lt;br /&gt;I don't write a test specification for myself, I just run through the examples I designed in the beginning. If I find any errors, I don't fix them right away, but write them down in a test log. When I have tested everything, I can get back to programming again. Since I have some automatic tests in place, it's usually easy to add some more tests that captures the errors.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-4129028437626971973?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/4129028437626971973/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=4129028437626971973' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/4129028437626971973'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/4129028437626971973'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2010/07/make-it-or-break-it.html' title='Make it or break it'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-6784159459658716563</id><published>2010-04-21T13:53:00.007+02:00</published><updated>2011-02-10T22:34:19.930+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Swing'/><title type='text'>Selecting columns or rows but not cells in JTable</title><content type='html'>Here is a snippet to enable selection of either columns or rows, but not cells, in a JTable:&lt;br /&gt;&lt;pre&gt;   public Integer findColumn(JTable table, Point p)&lt;br /&gt;   {&lt;br /&gt;      JTableHeader tableHeader = table.getTableHeader();&lt;br /&gt;      p.translate(-tableHeader.getX(), -tableHeader.getY());&lt;br /&gt;      if(p.x &gt;= 0 &amp;&amp; p.x &lt; tableHeader.getSize().width &amp;&amp;&lt;br /&gt;            p.y &gt;= 0 &amp;&amp; p.y &lt; tableHeader.getSize().height)&lt;br /&gt;      {&lt;br /&gt;         int x = p.x;&lt;br /&gt;         for(int i = 0; i &lt; table.getColumnCount(); i++)&lt;br /&gt;         {&lt;br /&gt;            String columnName = table.getColumnName(i);&lt;br /&gt;            TableColumn column = table.getColumn(columnName);&lt;br /&gt;            x -= column.getWidth();&lt;br /&gt;            if(x &lt; 0)&lt;br /&gt;            {&lt;br /&gt;               return column.getModelIndex();&lt;br /&gt;            }&lt;br /&gt;         }&lt;br /&gt;      }&lt;br /&gt;      &lt;br /&gt;      return null;&lt;br /&gt;   }&lt;br /&gt;   &lt;br /&gt;   public void enableRowSelection(final JTable table)&lt;br /&gt;   {&lt;br /&gt;      table.getTableHeader().addMouseListener(new MouseAdapter()&lt;br /&gt;      {&lt;br /&gt;         @Override&lt;br /&gt;         public void mouseClicked(MouseEvent e)&lt;br /&gt;         {&lt;br /&gt;            table.getSelectionModel().clearSelection();&lt;br /&gt;            table.setColumnSelectionAllowed(true);&lt;br /&gt;            table.setRowSelectionAllowed(false);&lt;br /&gt;            int column = findColumn(table, e.getPoint());&lt;br /&gt;            table.getColumnModel().getSelectionModel().&lt;br /&gt;               setSelectionInterval(column, column);&lt;br /&gt;         }&lt;br /&gt;      });&lt;br /&gt;      table.addMouseListener(new MouseAdapter()&lt;br /&gt;      {&lt;br /&gt;         @Override&lt;br /&gt;         public void mousePressed(MouseEvent e)&lt;br /&gt;         {&lt;br /&gt;            table.setColumnSelectionAllowed(false);&lt;br /&gt;            table.setRowSelectionAllowed(true);&lt;br /&gt;            table.getColumnModel().getSelectionModel().&lt;br /&gt;               clearSelection();&lt;br /&gt;         }&lt;br /&gt;      });&lt;br /&gt;   }&lt;/pre&gt;&lt;br /&gt;Some adjustment may be needed to enable selection of single/multiple columns or rows.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-6784159459658716563?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/6784159459658716563/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=6784159459658716563' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/6784159459658716563'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/6784159459658716563'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2010/04/selecting-columns-or-rows-but-not-cells.html' title='Selecting columns or rows but not cells in JTable'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-829412646284127877</id><published>2008-12-24T13:59:00.005+01:00</published><updated>2008-12-24T14:09:41.358+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Groovy'/><title type='text'>Refactoring can be easier in Groovy than in Java</title><content type='html'>I love Groovy for the productivity, but I am sceptical to maintenance and refactoring of large code bases in Groovy. Without type safety, you cannot detect refactoring errors at compile time, so you need very high coverage with unit tests.&lt;br /&gt;&lt;br /&gt;But the other day, I experienced something that got me thinking: I have a few Java classes and some Groovy scripts that call the Java code something like this:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;def var = javaObject.func()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I changed the return type of the Java method, and the Groovy code just kept on working. I didn't have to change it at all. Why? Because the Groovy code did not know the type I was returning. The dynamic nature of Groovy made refactoring easier, not harder!&lt;br /&gt;&lt;br /&gt;How can type safety make refactoring harder? Because type safety leads to duplication of information. In Java, the code above would look like this:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;String var = javaObject.func()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The return type "String" would be duplicated in calls to this method, in interfaces that specified this method, in subclasses, in variables, and so on. Not so in Groovy. The return type is declared only in one place.&lt;br /&gt;&lt;br /&gt;I always wondered why we designed programming languages different than databases. In databases, we try to avoid duplication of information. Duplication is a maintenance nightmare. But in programming, we duplicate information all the time. Not only with return types, but with boilerplate code, design patterns and class, variable and method names. In databases, we define immutable primary keys instead of refering to rows by values that could be modified. But in programming languages, we refer to everything by name. So when we want to change a name, we need a complicated "refactoring" tool to replace this name everywhere it occurs.&lt;br /&gt;&lt;br /&gt;I dream of a programming tool that let my type code like: "x = myObject.func1()" but under the hood it would create references to "myObject" and "func1" instead of resolving that at compile time. Then, there would be no "rename refactoring", just change the name and the reference would still be valid. The text should then be updated automatically.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-829412646284127877?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/829412646284127877/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=829412646284127877' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/829412646284127877'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/829412646284127877'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/12/refactoring-is-easier-in-groovy-than-in.html' title='Refactoring can be easier in Groovy than in Java'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-5265996841448141570</id><published>2008-12-04T22:25:00.014+01:00</published><updated>2008-12-04T23:32:55.962+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='HiberObjects'/><category scheme='http://www.blogger.com/atom/ns#' term='unit testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='JPA'/><title type='text'>How to test Java Persistence API in 5 minutes</title><content type='html'>I am the author of HiberObjects and want to give an example of how useful it can be.&lt;br /&gt;&lt;br /&gt;Yesterday, someone asked me a Java Persistence API (JPA) question: &lt;i&gt;If an object is removed from a bidirectional relation, do both objects have to be saved to update the database correctly?&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;I thought I knew the answer, but better test it first. This is how I did it:&lt;br /&gt;&lt;br /&gt;1) Design a class diagram with 2 persistent classes User and Membership and 1 unit test UserPersistentTest:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_BusLivqbx88/SThMHIXJ-sI/AAAAAAAAABk/R3rnq1ufJ0A/s1600-h/Classes.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 240px;" src="http://4.bp.blogspot.com/_BusLivqbx88/SThMHIXJ-sI/AAAAAAAAABk/R3rnq1ufJ0A/s400/Classes.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5276050648851937986" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;2) Initialize the database with some objects that I can test against: Right-click on the UserPersistentTest class and select Design Objects, then design objects like this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_BusLivqbx88/SThMqLKrC8I/AAAAAAAAABs/lb6BrbeidMY/s1600-h/Objects.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 374px; height: 49px;" src="http://2.bp.blogspot.com/_BusLivqbx88/SThMqLKrC8I/AAAAAAAAABs/lb6BrbeidMY/s400/Objects.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5276051250900306882" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;3) Save the diagram. This will generate JPA classes, a unit test and a helper class. The unit test will initialize the database with objects like this:&lt;br /&gt;&lt;pre&gt;   private void initObjects()&lt;br /&gt;   {&lt;br /&gt;      EntityManager entityManager = persistenceHelper.getEntityManager();&lt;br /&gt;      EntityTransaction tx = entityManager.getTransaction();&lt;br /&gt;      tx.begin();&lt;br /&gt;      user = new User();&lt;br /&gt;      membership = new Membership();&lt;br /&gt;      user.setName("Lars");&lt;br /&gt;      membership.setX(100);&lt;br /&gt;      entityManager.persist(user);&lt;br /&gt;      entityManager.persist(membership);&lt;br /&gt;      user.getMembership().add(membership);&lt;br /&gt;      membership.getUser().add(user);&lt;br /&gt;      tx.commit();&lt;br /&gt;      entityManager.close();&lt;br /&gt;   }&lt;/pre&gt;4) Implement the test method:&lt;br /&gt;&lt;pre&gt;   public void test1() throws Exception&lt;br /&gt;   {&lt;br /&gt;      EntityManager em = persistenceHelper.getEntityManager();&lt;br /&gt;      em.getTransaction().begin();&lt;br /&gt;      User user1 = (User) em.createQuery("from User").getSingleResult();&lt;br /&gt;      assertEquals(1, user1.getMembership().size());&lt;br /&gt;      &lt;br /&gt;      Membership membership1 = (Membership) em.createQuery("from Membership").getSingleResult();&lt;br /&gt;      assertEquals(1, membership1.getUser().size());&lt;br /&gt;      &lt;br /&gt;      assertSame(membership1, user1.getMembership().iterator().next());&lt;br /&gt;      assertSame(user1, membership1.getUser().iterator().next());&lt;br /&gt;      &lt;br /&gt;      user1.removeMembership(membership1);&lt;br /&gt;      assertEquals(0, user1.getMembership().size());&lt;br /&gt;      assertEquals(0, membership1.getUser().size());&lt;br /&gt;      em.getTransaction().commit();&lt;br /&gt;      &lt;br /&gt;      em.getTransaction().begin();&lt;br /&gt;      User user2 = (User) em.createQuery("from User").getSingleResult();&lt;br /&gt;      assertEquals(0, user2.getMembership().size());&lt;br /&gt;      Membership membership2 = (Membership) em.createQuery("from Membership").getSingleResult();&lt;br /&gt;      assertEquals(0, membership2.getUser().size());&lt;br /&gt;      em.getTransaction().commit();&lt;br /&gt;   }&lt;/pre&gt;5) Run as JUnit test:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_BusLivqbx88/SThNeqc0qHI/AAAAAAAAAB0/ylfkRDWqfTI/s1600-h/Test.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 366px; height: 280px;" src="http://1.bp.blogspot.com/_BusLivqbx88/SThNeqc0qHI/AAAAAAAAAB0/ylfkRDWqfTI/s400/Test.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5276052152651130994" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This demonstrates the answer to the question: You don't need to save any of the objects explicitly if you change them in a transaction.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-5265996841448141570?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/5265996841448141570/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=5265996841448141570' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/5265996841448141570'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/5265996841448141570'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/12/how-to-test-java-persistence-api-in-5.html' title='How to test Java Persistence API in 5 minutes'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_BusLivqbx88/SThMHIXJ-sI/AAAAAAAAABk/R3rnq1ufJ0A/s72-c/Classes.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-5782022208255711696</id><published>2008-07-19T22:27:00.007+02:00</published><updated>2008-07-19T23:18:15.298+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Eclipse'/><title type='text'>My favorite Eclipse view</title><content type='html'>&lt;p&gt;Have you discovered the Display view in the Eclipse debugger? It is my personal favorite. In it, you can execute any Java code in the context of a running debugger.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;To open this view, select Window &gt; Show View &gt; Display.&lt;/p&gt;&lt;p&gt;When the debugger hits a break point, you can type some Java code in the Display view. The Java code can use the local variables in the currently selected frame in the Debug view.&lt;/p&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_BusLivqbx88/SIJZhm-NljI/AAAAAAAAABE/2akwhIN2e5Q/s1600-h/Display+View.png"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_BusLivqbx88/SIJZhm-NljI/AAAAAAAAABE/2akwhIN2e5Q/s400/Display+View.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5224836951635236402" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;To execute the code and display the returned value, push the button with a "J":&lt;/p&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_BusLivqbx88/SIJZhncF6qI/AAAAAAAAABM/gFxWhIwQlwk/s1600-h/Show+Result.png"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_BusLivqbx88/SIJZhncF6qI/AAAAAAAAABM/gFxWhIwQlwk/s400/Show+Result.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5224836951760562850" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;If you just want to execute some code that doesn't return a value, push the button with an arrow "&gt;" and a "J":&lt;/p&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_BusLivqbx88/SIJZh-3X5pI/AAAAAAAAABU/yX1xrsdkvFw/s1600-h/Display+View+2.png"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_BusLivqbx88/SIJZh-3X5pI/AAAAAAAAABU/yX1xrsdkvFw/s400/Display+View+2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5224836958049003154" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;The standard output will be printed to the Console view:&lt;/p&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_BusLivqbx88/SIJZh0NEdEI/AAAAAAAAABc/DbAu_QHVo7E/s1600-h/Execute.png"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_BusLivqbx88/SIJZh0NEdEI/AAAAAAAAABc/DbAu_QHVo7E/s400/Execute.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5224836955187213378" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;This is incredibly useful! You can for instance run some code in a debugger and run some complex debugging code, or prototype some code for a new method.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-5782022208255711696?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/5782022208255711696/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=5782022208255711696' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/5782022208255711696'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/5782022208255711696'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/07/my-favorite-eclipse-view.html' title='My favorite Eclipse view'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_BusLivqbx88/SIJZhm-NljI/AAAAAAAAABE/2akwhIN2e5Q/s72-c/Display+View.png' height='72' width='72'/><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-6304714233171630418</id><published>2008-07-13T21:00:00.010+02:00</published><updated>2008-12-04T23:33:19.945+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><title type='text'>World War 2, Rationalism and Agile Development</title><content type='html'>One of the central values of the &lt;a href="http://agilemanifesto.org/"&gt;Agile Manifesto&lt;/a&gt; is &lt;i&gt;Responding to change over following a plan.&lt;/i&gt; This doesn't mean that it's not agile to make a plan. You should have a plan, but you should realize the limitations of the plan.&lt;br /&gt;&lt;br /&gt;Are there any parallels to this in history?&lt;br /&gt;&lt;br /&gt;The Soviet Union had 5-year plans. &lt;a href="http://en.wikipedia.org/wiki/Joseph_Stalin"&gt;Stalin&lt;/a&gt; was quite succesful in industrializing the rural country in a short span of time. But his methods did not work well in the battlefield. In the 2nd world war, the Soviet suffered severe losses in the beginning.&lt;br /&gt;&lt;br /&gt;You can plan how to build and manage a factory, because the complexity is limited. But a war is far more complex. You cannot predict what the enemy is going to do. This doesn't mean that you should go to war without a plan. Far from it, you should plan as much as possible, but be prepared to improvize when something unexpected happens. As &lt;a href="http://en.wikipedia.org/wiki/Dwight_D._Eisenhower"&gt;Dwight D. Eisenhower&lt;/a&gt;, a successful American general in World War 2, said: "Plans are nothing; planning is everything."&lt;br /&gt;&lt;br /&gt;Building software is also complex. But some managers try to run a software development organization as a factory. They treat their employees like cog-wheels. They think that Employee #1093 can take over the responsibilities of Employee #835 with just some training. Or that 4 programmers can do in 3 months what one programmer can do in 1 year. It doesn't work like that. If software development was so predictable, then robots could do it. But they can't.&lt;br /&gt;&lt;br /&gt;I don't like being treated like a cog-wheel. I am not Employee #1093, I am me. I am unique with my own weaknesses and strengths. If I am treated like a replaceable part, I may start to behave like one. Then, I will only do as I'm told, nothing more, nothing less. If nobody listens to my ideas, I will stop thinking creatively, and just do things the way we always did.&lt;br /&gt;&lt;br /&gt;Since I don't want to become mediocre, I avoid environments like that. I seek challenging environments where my ideas are heard and I am given responsibility to work as I think is best.&lt;br /&gt;&lt;br /&gt;I think that Churchill, the Prime Minister of Great Britain during WW2, was agile too. He was not afraid to change. He said: "To improve is to change; to be perfect is to change often.&lt;br /&gt;&lt;br /&gt;He also said, "However beautiful the strategy, you should occasionally look at the results." That sounds like iterative development to me. You make a plan, and then review the results now and then to see how it works. (See more fantastic quotes of Churchill &lt;a href="http://www.brainyquote.com/quotes/authors/w/winston_churchill.html"&gt;here&lt;/a&gt;.)&lt;br /&gt;&lt;br /&gt;So how about &lt;a href="http://en.wikipedia.org/wiki/Hitler"&gt;Hitler&lt;/a&gt;? How agile was he? Not agile at all. He had a great vision and a plan that seemed to work very well in the beginning. But as we all know, he failed. I think this is similar to a waterfall project: He was 80% finished with conquering Western Europe, and 75% finished with Eastern Europe. If he had run the war more iteratively, he should have completed Western Europe first, and then started on Eastern Europe. Thank God, he didn't!&lt;br /&gt;&lt;br /&gt;What did Hitler and Stalin have in common? Both nazis and communists were &lt;a href="http://en.wikipedia.org/wiki/Rationalism"&gt;rationalists&lt;/a&gt; with a strong belief in their own enlightened minds. I dare say that Churchill and Eisenhower had a more humble attitude, accepting that they could not make perfect plans. I would also say that Churchill was &lt;a href="http://en.wikipedia.org/wiki/Empiricism"&gt;empiric&lt;/a&gt; in that he wanted to review the results of his plan.&lt;br /&gt;&lt;br /&gt;Rationalists think they can plan everything. Empirists believes in experimental evidence. I think that waterfall development is a result of rationalist thinking, but it doesn't work well, because our minds are limited. We cannot predict everything, but we are capable of planning to a large degree. Therefore, I believe iterative development where you make a plan but also review the results regularly is the most effective way.&lt;br /&gt;&lt;br /&gt;PS: I am not a historian nor a philosopher, so please, enlighten me if I got something wrong.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-6304714233171630418?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/6304714233171630418/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=6304714233171630418' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/6304714233171630418'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/6304714233171630418'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/07/reflections-on-ww2-rationalism-and.html' title='World War 2, Rationalism and Agile Development'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-3430708550466109717</id><published>2008-05-17T23:24:00.004+02:00</published><updated>2011-02-10T22:34:35.282+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='examples'/><title type='text'>Use examples to make your code easier to understand</title><content type='html'>Programmers are used to abstract thinking. To program is to generalize: A method is a general specification of what to do during execution. A class is a general specification of objects. A superclass is a generalization of several classes.&lt;br /&gt;&lt;br /&gt;Altough our minds are capable of abstract thinking, concrete thinking is much easier, and concrete examples are the foundation for abstractions. For instance, when we were children, our parents didn't try to teach us about cars by explaining to us cars are and what they can do. Instead, they just pointed at a car that was driving by and said ”Look, a car!” When they had done that a number of times, we knew what a car was.&lt;br /&gt;&lt;br /&gt;Another example is prejudice. We all have prejudices, because this is the way our minds work. If we have met a few people from Denmark in our lives, and those people were friendly, we ”know” that Danes are friendly. And this works even stronger for negative prejudices.&lt;br /&gt;&lt;br /&gt;My point is that we learn by examples. Einstein said that &lt;span class="Apple-style-span" style="font-style: italic;"&gt;&lt;span class="Apple-style-span" style="color: rgb(51, 51, 255);"&gt;examples is not another way to teach, it is the only way.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;As programmers, we are highly skilled in abstract thinking and expressing ourselves in programming languages. But when the code becomes too complex, we try to comprehend it by making examples in our minds of what the code would do in different circumstances. We imagine or write down the values of different variables and what the behavior of different tests and loops will be with those values.&lt;br /&gt;&lt;br /&gt;And when it becomes too hard to imagine what is actually going on by reading the abstract code, we use debuggers for help. A debugger is not abstract. It gives a concrete description of what is actually happening at runtime. It gives information on how methods execute in specific instances and which objects exist at a specific moment. This concrete information is so much easier to understand than the abstract code.&lt;div&gt;&lt;br /&gt;Therefore, it's a good idea to design concrete examples of objects and scenarios before implementing the abstract classes.&lt;br /&gt;&lt;br /&gt;Unit tests are exactly such examples. They make it easier to understand what happens at runtime. And a well designed unit test is the best way to explain to somebody what the code does. If you ever had to take over somebody else's code, you know how hard it can be to understand it. Where do you start? Unit tests is a good place to start.&lt;br /&gt;&lt;br /&gt;So, my suggestion is to think of unit tests as documentation. Write unit tests that are easy to read and that explain the intent of the code.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-3430708550466109717?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/3430708550466109717/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=3430708550466109717' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/3430708550466109717'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/3430708550466109717'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/05/use-examples-to-make-your-code-easier.html' title='Use examples to make your code easier to understand'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-5286451774397502238</id><published>2008-03-08T15:16:00.002+01:00</published><updated>2008-03-08T15:28:13.393+01:00</updated><title type='text'>I believe in Java for future web applications</title><content type='html'>Will Java become the dominant platform for web applications? Actually, I think it has a good chance.&lt;br /&gt;&lt;br /&gt;How will the future web applications be? I think we can expect the following:&lt;br /&gt;* Highly interactive&lt;br /&gt;* Complex, more work will be done on the client&lt;br /&gt;* Collaborative, not only for games, but also for other applications&lt;br /&gt;* Mobile&lt;br /&gt;* Location aware&lt;br /&gt;&lt;br /&gt;AJAX is fantastic compared to "web 1.0", and technology like GWT (Java) makes it relatively easy to develop complex user interfaces. But HTML and HTTP still sucks as an application platform, something they were never meant to be. I believe that if Microsoft hadn't managed to kill the Applet, Java would be the dominant platform for more complex web applications today.&lt;br /&gt;&lt;br /&gt;I think Consumer JRE can be that platform. Java can be used to make complex, interactive applications that communicate with each other client-to-client.&lt;br /&gt;&lt;br /&gt;This doesn't mean we have to implement in Java. When consumers have a JRE ready, there is nothing stopping us from developing applications in Groovy, JRuby and Scala and deliver them over the web.&lt;br /&gt;&lt;br /&gt;I don't know much about Flash, but I am sure it is at least as good as Java for making &lt;i&gt;flashy&lt;/i&gt; applications. But when it comes to complex collaborative and mobile applications, I think Java has the upper hand.&lt;br /&gt;&lt;br /&gt;And Java has one more powerful force behind it: The community. The Java community has developed hundreds of libraries and frameworks. Too many, perhaps. But this community is capable of revolutionizing the way web applications are developed in ways we cannot predict now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-5286451774397502238?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/5286451774397502238/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=5286451774397502238' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/5286451774397502238'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/5286451774397502238'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/03/i-believe-in-java-for-future-web.html' title='I believe in Java for future web applications'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-8576095089773382157</id><published>2008-02-26T01:18:00.002+01:00</published><updated>2008-02-26T01:28:57.747+01:00</updated><title type='text'>Passwords in phpBB 3</title><content type='html'>Here is a port of phpBB3's password handling to Java.&lt;br /&gt; &lt;br /&gt;&lt;pre&gt;import java.io.UnsupportedEncodingException;&lt;br /&gt;import java.security.GeneralSecurityException;&lt;br /&gt;import java.security.MessageDigest;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Port of phpBB3 password handling to Java. &lt;br /&gt; * See phpBB3/includes/functions.php&lt;br /&gt; * &lt;br /&gt; * @author lars&lt;br /&gt; */&lt;br /&gt;public class PHPBB3Password {&lt;br /&gt;  private static final int PHP_VERSION = 4;&lt;br /&gt;  private String itoa64 = &lt;br /&gt;"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";&lt;br /&gt;&lt;br /&gt;  public String phpbb_hash(String password) {&lt;br /&gt;    String random_state = unique_id();&lt;br /&gt;    String random = "";&lt;br /&gt;    int count = 6;&lt;br /&gt;&lt;br /&gt;    if (random.length() &lt; count) {&lt;br /&gt;      random = "";&lt;br /&gt;&lt;br /&gt;      for (int i = 0; i &lt; count; i += 16) {&lt;br /&gt;        random_state = md5(unique_id() + random_state);&lt;br /&gt;        random += pack(md5(random_state));&lt;br /&gt;      }&lt;br /&gt;      random = random.substring(0, count);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    String hash = _hash_crypt_private(&lt;br /&gt;      password, _hash_gensalt_private(random, itoa64));&lt;br /&gt;    if (hash.length() == 34)&lt;br /&gt;      return hash;&lt;br /&gt;&lt;br /&gt;    return md5(password);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private String unique_id() {&lt;br /&gt;    return unique_id("c");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // global $config;&lt;br /&gt;  // private boolean dss_seeded = false;&lt;br /&gt;&lt;br /&gt;  private String unique_id(String extra) {&lt;br /&gt;    // TODO Generate something random here.&lt;br /&gt;    return "1234567890abcdef";&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private String _hash_gensalt_private(String input, String itoa64) {&lt;br /&gt;    return _hash_gensalt_private(input, itoa64, 6);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private String _hash_gensalt_private(&lt;br /&gt;    String input, String itoa64, int iteration_count_log2) {&lt;br /&gt;    if (iteration_count_log2 &lt; 4 || iteration_count_log2 &gt; 31) {&lt;br /&gt;      iteration_count_log2 = 8;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    String output = "$H$";&lt;br /&gt;    output += itoa64.charAt(&lt;br /&gt;      Math.min(iteration_count_log2 +&lt;br /&gt;      ((PHP_VERSION &gt;= 5) ? 5 : 3), 30));&lt;br /&gt;    output += _hash_encode64(input, 6);&lt;br /&gt;&lt;br /&gt;    return output;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /**&lt;br /&gt;   * Encode hash&lt;br /&gt;   */&lt;br /&gt;  private String _hash_encode64(String input, int count) {&lt;br /&gt;    String output = "";&lt;br /&gt;    int i = 0;&lt;br /&gt;&lt;br /&gt;    do {&lt;br /&gt;      int value = input.charAt(i++);&lt;br /&gt;      output += itoa64.charAt(value &amp; 0x3f);&lt;br /&gt; &lt;br /&gt;      if (i &lt; count)&lt;br /&gt;        value |= input.charAt(i) &lt;&lt; 8;&lt;br /&gt; &lt;br /&gt;      output += itoa64.charAt((value &gt;&gt; 6) &amp; 0x3f);&lt;br /&gt; &lt;br /&gt;      if (i++ &gt;= count)&lt;br /&gt;        break;&lt;br /&gt; &lt;br /&gt;      if (i &lt; count)&lt;br /&gt;        value |= input.charAt(i) &lt;&lt; 16;&lt;br /&gt; &lt;br /&gt;      output += itoa64.charAt((value &gt;&gt; 12) &amp; 0x3f);&lt;br /&gt; &lt;br /&gt;      if (i++ &gt;= count)&lt;br /&gt;        break;&lt;br /&gt; &lt;br /&gt;      output += itoa64.charAt((value &gt;&gt; 18) &amp; 0x3f);&lt;br /&gt;    } while (i &lt; count);&lt;br /&gt;&lt;br /&gt;    return output;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  String _hash_crypt_private(String password, String setting) {&lt;br /&gt;      String output = "*";&lt;br /&gt;&lt;br /&gt;      // Check for correct hash&lt;br /&gt;      if (!setting.substring(0, 3).equals("$H$"))&lt;br /&gt;        return output;&lt;br /&gt;&lt;br /&gt;      int count_log2 = itoa64.indexOf(setting.charAt(3));&lt;br /&gt;      if (count_log2 &lt; 7 || count_log2 &gt; 30)&lt;br /&gt;        return output;&lt;br /&gt;&lt;br /&gt;      int count = 1 &lt;&lt; count_log2;&lt;br /&gt;      String salt = setting.substring(4, 12);&lt;br /&gt;      if (salt.length() != 8)&lt;br /&gt;        return output;&lt;br /&gt;&lt;br /&gt;      String m1 = md5(salt + password);&lt;br /&gt;      String hash = pack(m1);&lt;br /&gt;      do {&lt;br /&gt;        hash = pack(md5(hash + password));&lt;br /&gt;      } while (--count &gt; 0);&lt;br /&gt;&lt;br /&gt;      output = setting.substring(0, 12);&lt;br /&gt;      output += _hash_encode64(hash, 16);&lt;br /&gt;&lt;br /&gt;      return output;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public boolean phpbb_check_hash(&lt;br /&gt;    String password, String hash) {&lt;br /&gt;      if (hash.length() == 34)&lt;br /&gt;        return _hash_crypt_private(password, hash).equals(hash);&lt;br /&gt;      else&lt;br /&gt;        return md5(password).equals(hash);&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;  public static String md5(String data) {&lt;br /&gt;    try {&lt;br /&gt;      byte[] bytes = data.getBytes("ISO-8859-1");&lt;br /&gt;      MessageDigest md5er = MessageDigest.getInstance("MD5");&lt;br /&gt;      byte[] hash = md5er.digest(bytes);&lt;br /&gt;      return bytes2hex(hash);&lt;br /&gt;    } catch (GeneralSecurityException e) {&lt;br /&gt;      throw new RuntimeException(e);&lt;br /&gt;    } catch (UnsupportedEncodingException e) {&lt;br /&gt;      throw new RuntimeException(e);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  static int hexToInt(char ch) {&lt;br /&gt;    if(ch &gt;= '0' &amp;&amp; ch &lt;= '9')&lt;br /&gt;      return ch - '0';&lt;br /&gt;  &lt;br /&gt;    ch = Character.toUpperCase(ch);&lt;br /&gt;    if(ch &gt;= 'A' &amp;&amp; ch &lt;= 'F')&lt;br /&gt;      return ch - 'A' + 0xA;&lt;br /&gt;  &lt;br /&gt;    throw new IllegalArgumentException("Not a hex character: " + ch);&lt;br /&gt;  }&lt;br /&gt; &lt;br /&gt;  private static String bytes2hex(byte[] bytes) {&lt;br /&gt;    StringBuffer r = new StringBuffer(32);&lt;br /&gt;    for (int i = 0; i &lt; bytes.length; i++) {&lt;br /&gt;      String x = Integer.toHexString(bytes[i] &amp; 0xff);&lt;br /&gt;      if (x.length() &lt; 2)&lt;br /&gt;        r.append("0");&lt;br /&gt;      r.append(x);&lt;br /&gt;    }&lt;br /&gt;    return r.toString();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  static String pack(String hex) {&lt;br /&gt;    StringBuffer buf = new StringBuffer();&lt;br /&gt;    for(int i = 0; i &lt; hex.length(); i += 2) {&lt;br /&gt;      char c1 = hex.charAt(i);&lt;br /&gt;      char c2 = hex.charAt(i+1);&lt;br /&gt;      char packed = (char) (hexToInt(c1) * 16 + hexToInt(c2));&lt;br /&gt;      buf.append(packed);&lt;br /&gt;    }&lt;br /&gt;    return buf.toString();&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-8576095089773382157?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/8576095089773382157/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=8576095089773382157' title='16 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/8576095089773382157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/8576095089773382157'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/02/passwords-in-phpbb-3.html' title='Passwords in phpBB 3'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>16</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-3430209257726887287</id><published>2008-02-22T12:08:00.005+01:00</published><updated>2008-12-04T23:33:52.466+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><title type='text'>Agile practices don't primarily solve problems</title><content type='html'>But they sure help detecting problems early. And that's extremely valuable.&lt;br /&gt;&lt;br /&gt;For instance, iterative development with deliveries early in the project will test the technical feasibility and the development speed. But perhaps most important, we will get feedback on the functionality from the clients, to verify if we have understood their problem correctly.&lt;br /&gt;&lt;br /&gt;Failing in any of these areas will lead to project failure. And it is better to discover this when 20% of the budget is used than when 90% of the budget is used. First, you reduce your losses. And even better, you have a fair chance of fixing the project and turning it into success.&lt;br /&gt;&lt;br /&gt;Many agile projects succeed not because they are more productive, but because they discover problems in early iteations and then reduce the requirements.&lt;br /&gt;&lt;br /&gt;Unit testing is another practice that makes you aware of problems early. This makes it cheaper to fix problems, but it also gives confidence, so you dear restructure code when necessary.&lt;br /&gt;&lt;br /&gt;Another agile practice is close communication with business experts, preferably face to face. The non-agile alternative is written requirements. The problem is that developers misunderstand what the business experts want. That's inevitable. What we need, are mechanisms to discover misunderstandings. That mechanism is called &lt;i&gt;feedback&lt;/i&gt;. The business expert explain what they want, and the developer explain how they understood that to the business expert, which confirms that this was correct. That's easier to do face to face than with written documents.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-3430209257726887287?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/3430209257726887287/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=3430209257726887287' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/3430209257726887287'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/3430209257726887287'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/02/agile-practices-dont-solve-any-problems.html' title='Agile practices don&apos;t primarily solve problems'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-1149284681504947260</id><published>2008-02-11T21:57:00.001+01:00</published><updated>2008-12-04T23:34:28.216+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Groovy'/><title type='text'>Hungarian notation and Groovy</title><content type='html'>Comments and types are metadata and strictly not necessary to make a program, according to a &lt;a href="http://steve-yegge.blogspot.com/2008/02/portrait-of-n00b.html"&gt;popular post&lt;/a&gt;. Yes, they are not necessary to communicate with the computer, but that's not what we are trying to do here. For all but the smallest projects, we are communicating with &lt;i&gt;people&lt;/i&gt;. The code should be understandable to others. And it doesn't hurt if it's understandable to ourselves either.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Hungarian_notation"&gt;Hungarian notation&lt;/a&gt; was a popular in C to show the type of a variable in its name, for instance:&lt;pre&gt;int nSize;&lt;br /&gt;char cChar;&lt;/pre&gt;&lt;br /&gt;I never liked this, because if I change the type of a variable I would also have to change its name everywhere. But with dynamically typed languages, I some times get in trouble because I forget what type a variable is. So, I started to think if maybe Hungarian notation could be useful.&lt;br /&gt;&lt;br /&gt;For example, consider the following dynamically typed Groovy code:&lt;pre&gt;(1) def x = "100"&lt;/pre&gt;&lt;br /&gt;the following statically typed Java code:&lt;pre&gt;(2) String x = "100";&lt;/pre&gt;&lt;br /&gt;and this statically typed Java code:&lt;pre&gt;(3) int x = "100";&lt;/pre&gt;&lt;br /&gt;Which is most easy to understand?&lt;br /&gt;(3) is easily recognized by both humans and the compiler as an error.&lt;br /&gt;(1) and (2) are valid code. But 20 lines down the code, you have forgotten what type x is and write:&lt;br /&gt;&lt;pre&gt;x++&lt;/pre&gt;&lt;br /&gt;Then (2) will give you a compiler error, but (1) won't be detected until runtime.&lt;br /&gt;&lt;br /&gt;Does this mean I'm against dynamically typing? No. But if we remove the static type checking, I think we should use other means to communicate that information. For instance, it is a bad idea to call a String x. This would be much better:&lt;br /&gt;&lt;pre&gt;def s = "100"&lt;/pre&gt;&lt;br /&gt;In Groovy, I often use maps instead of creating a new class:&lt;pre&gt;def map = [:]&lt;br /&gt;map.x = 100&lt;br /&gt;map.y = 200&lt;/pre&gt;&lt;br /&gt;How am I supposed to remember what the map contains down the road?&lt;br /&gt;&lt;br /&gt;I don't want to remember, I want the map to remind me! I have enough things to think about, I don't want to remember the type of every variable too!&lt;br /&gt;&lt;br /&gt;I have a lame technique I use to not forget important things: I try to make them remind me instead of having to remember them. For instance, if I take off the lid to fill gas in the car, I put the car keys by the lid. That way, it's impossible for me to forget it. I cannot drive off without seeing the lid and remember to put it on. I use similar techniques at work. This reduces stress and helps me to concentrate, because I don't need to remember so much.&lt;br /&gt;&lt;br /&gt;Applying this technique to Groovy gives this code:&lt;pre&gt;def point = [:]&lt;br /&gt;point.x = 100&lt;br /&gt;point.y = 200&lt;/pre&gt;&lt;br /&gt;So, I am not advocating Hungarian notation, but good naming conventions that communicate what we need to know.&lt;br /&gt;&lt;br /&gt;Programming is more about communication than how fast you can crank out code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-1149284681504947260?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/1149284681504947260/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=1149284681504947260' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/1149284681504947260'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/1149284681504947260'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/02/hungarian-notation-and-groovy.html' title='Hungarian notation and Groovy'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-1107521984201322694</id><published>2008-02-06T17:29:00.000+01:00</published><updated>2008-02-06T17:56:56.531+01:00</updated><title type='text'>The Pessimistic Programmer</title><content type='html'>I decided to change the title of this blog to "The Pessimistic Programmer". Why? Am I a depressed person that thinks nothing will work? No, I am an optimist in life. Something good is going to happen today :-) But in programming, something will surely go wrong.&lt;br /&gt;&lt;br /&gt;I don't actually view this as pessimism, but as realism. I want to be prepared for the worst that can possibly happen. Hope for the best, prepare for the worst. But my wife explained to me that pessimists always say that they are just being realistic. So, I might as well face it: I am a pessimist.&lt;br /&gt;&lt;br /&gt;I think a good programmer needs to be pessimistic; always thinking about what can go wrong and how to prevent it. I don't say that I am a good programmer myself. No, I make far too many mistakes for that. But I have learnt how to manage my mistakes with testing and double checking.&lt;br /&gt;&lt;br /&gt;Über-programmers can manage well without being pessimistic. They have total overview of the code and all consequences of changes. But I'm talking about us mere mortals. But if you are an über-programmer, you should be pessimistic about what the next guy will do to your code. Place some comments and unit tests in there, to keep him out of trouble! &lt;br /&gt;&lt;br /&gt;An optimistic programmer doesn't see the need for unit tests. He will run the program and be satisfied when it runs one time without errors. He will say things like "What can possibly go wrong?" Or if something goes wrong anyway, "It must be an error in the input from that other module".&lt;br /&gt;&lt;br /&gt;Haven't we all been there? Suddenly, I feel so old...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-1107521984201322694?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/1107521984201322694/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=1107521984201322694' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/1107521984201322694'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/1107521984201322694'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/02/pessimistic-programmer.html' title='The Pessimistic Programmer'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-2175009627994046676</id><published>2008-02-06T08:22:00.000+01:00</published><updated>2008-02-06T08:39:40.499+01:00</updated><title type='text'>Passwords in SMF 1.1.4</title><content type='html'>I'm posting this in case someone else are struggling with SMF password encrypting like I did.&lt;br /&gt;&lt;br /&gt;SMF 1.1.4 uses SHA-1 with a salt. You would think that the passwordSalt in the database is used as the salt, but it isn't. That's probably a field that was used in old versions. Instead, the membername is used as the salt, but *before* the password, not *after*, as most search results indicated.&lt;br /&gt;&lt;br /&gt;I finally found &lt;a href="http://paste.lisp.org/display/13702"&gt;this link&lt;/a&gt; to a php file that is *not* in the distribution.&lt;br /&gt;&lt;pre&gt;function smf_registerMember(&lt;br /&gt;[...]&lt;br /&gt;        $register_vars = array(&lt;br /&gt;                'memberName' =&gt; "'$username'",&lt;br /&gt;                'realName' =&gt; "'$username'",&lt;br /&gt;                'passwd' =&gt; '\'' . sha1(strtolower($username) . $password) . '\'',&lt;br /&gt;                'passwordSalt' =&gt; '\'' . substr(md5(rand()), 0, 4) . '\'',&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So I tried this Java code:&lt;pre&gt;sha1(username.toLowerCase() + password);&lt;/pre&gt; That worked!&lt;br /&gt;&lt;br /&gt;Here is the source for the sha1 method:&lt;pre&gt;   public static String sha1(String data)&lt;br /&gt;   {&lt;br /&gt;      byte[] bytes = data.getBytes();&lt;br /&gt;      try&lt;br /&gt;      {&lt;br /&gt;         MessageDigest md5er = MessageDigest.getInstance("SHA-1");&lt;br /&gt;         byte[] hash = md5er.digest(bytes);&lt;br /&gt;         return bytes2hex(hash);&lt;br /&gt;      }&lt;br /&gt;      catch (GeneralSecurityException e)&lt;br /&gt;      {&lt;br /&gt;         throw new RuntimeException(e);&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   private static String bytes2hex(byte[] bytes)&lt;br /&gt;   {&lt;br /&gt;      StringBuffer r = new StringBuffer(32);&lt;br /&gt;      for (int i = 0; i &lt; bytes.length; i++)&lt;br /&gt;      {&lt;br /&gt;         String x = Integer.toHexString(bytes[i] &amp; 0xff);&lt;br /&gt;         if (x.length() &lt; 2)&lt;br /&gt;            r.append("0");&lt;br /&gt;         r.append(x);&lt;br /&gt;      }&lt;br /&gt;      return r.toString();&lt;br /&gt;   }&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-2175009627994046676?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/2175009627994046676/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=2175009627994046676' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/2175009627994046676'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/2175009627994046676'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/02/passwords-in-smf-114.html' title='Passwords in SMF 1.1.4'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-17677979981155746</id><published>2008-01-25T00:14:00.001+01:00</published><updated>2011-02-10T22:34:58.251+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='examples'/><title type='text'>Example Driven Development and Unit testing</title><content type='html'>In a project I once worked, we were required by company standards to write formal test specifications for manual testing. It was a lot of overhead to write these Word documents and get them through the bureaucracy. Finally, we proposed to write the test specifications as comments inside functional unit tests. That way, we could maintain the test documents easily. We saved a lot of work, and the documents got higher quality because it was easier to update them. And it really helped us to write good unit tests that covered the functionality.&lt;br /&gt;&lt;br /&gt;In this post, I will take this approach one step further to show how unit tests and manual tests can be unified.&lt;br /&gt;&lt;br /&gt;In an earlier post &lt;a href="http://larsho.blogspot.com/2008/01/example-driven-development.html"&gt;Example driven development&lt;/a&gt;, I argued that a few simple examples can be used for requirements, manual testing and unit testing. I don't say that a few examples are sufficient as a requirements specification, but they may be in relatively simple projects. And it is far better than nothing. Examples help you to think clearly, and to communicate accurately with others. Don't we all use examples when we try to explain something? That's the best way to explain something anyway.&lt;br /&gt;&lt;br /&gt;The problem is that it is hard to keep the examples up to date, and then they lose the value they had for communication with the client and manual testing.&lt;br /&gt;&lt;br /&gt;But if we implement the examples as unit tests, we completely avoid this problem! As long as the unit tests pass, the examples will be in sync with the code. And if the client changes the requirements, we modify the unit tests, and then implement the changes until the unit tests pass.&lt;br /&gt;&lt;br /&gt;Unit tests are hard to read for non-programmers, but if we put a lot of effort into it, we can make them readable. They don't need to be writable, as programmers will write them.&lt;br /&gt;&lt;br /&gt;Here is an example of a test of a servlet that generates licenses:&lt;pre&gt;   public void testGenerateLicense() throws Exception&lt;br /&gt;   {&lt;br /&gt;      // Call the servlet.&lt;br /&gt;      InputParams params = new InputParams();&lt;br /&gt;      params.productId = "123456";&lt;br /&gt;      params.quantity = 1;&lt;br /&gt;      params.firstName = "Lars";&lt;br /&gt;      params.lastName = "Høidahl";&lt;br /&gt;      params.email = "lars@mycompany.com";&lt;br /&gt;      params.company = "Object Generation";&lt;br /&gt;      params.country = "Sweden";&lt;br /&gt;      File licenseFile = servlet.generateLicense(params);&lt;br /&gt;      &lt;br /&gt;      // Check the returned file.&lt;br /&gt;      assertTrue("Attachment is a license file",&lt;br /&gt;            licenseFile.getName().endsWith(".lic"));&lt;br /&gt;      &lt;br /&gt;      // Check that 1 user was created in the database.&lt;br /&gt;      List&lt;User&gt; users = userDatabase.getAllUsers();&lt;br /&gt;      assertEquals("Users", 1, users.size());&lt;br /&gt;      User user = users.get(users.size()-1);&lt;br /&gt;      assertEquals("User name", "Lars", user.getUsername());&lt;br /&gt;      assertEquals("Email", "lars@mycompany.com", user.getEmail());&lt;br /&gt;      assertEquals("Country", "Sweden", user.getCountry());&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public void testGenerateMultipleLicenses() throws Exception&lt;br /&gt;   {&lt;br /&gt;      // Call the servlet.&lt;br /&gt;      InputParams params = new InputParams();&lt;br /&gt;      params.productId = "123456";&lt;br /&gt;      params.quantity = 3;&lt;br /&gt;      params.firstName = "Lars";&lt;br /&gt;      params.lastName = "Høidahl";&lt;br /&gt;      params.email = "lars@mycompany.com";&lt;br /&gt;      params.company = "Object Generation";&lt;br /&gt;      params.country = "Sweden";&lt;br /&gt;      File licenseFile = servlet.generateLicense(params);&lt;br /&gt;      &lt;br /&gt;      // Check the returned file.&lt;br /&gt;      assertTrue("Attachment is a zip file",&lt;br /&gt;            licenseFile.getName().endsWith(".zip"));&lt;br /&gt;      ZipFile zipFile = new ZipFile(licenseFile);&lt;br /&gt;      assertEquals("Number of entries", 3, zipFile.size());&lt;br /&gt;      &lt;br /&gt;      // Check that 3 users were created in the database.&lt;br /&gt;      List&lt;User&gt; users = userDatabase.getAllUsers();&lt;br /&gt;      assertEquals("Users", 3, users.size());&lt;br /&gt;      User user = users.get(users.size()-1);&lt;br /&gt;      assertEquals("User name", "Lars", user.getUsername());&lt;br /&gt;      assertEquals("Email", "lars@mycompany.com", user.getEmail());&lt;br /&gt;      assertEquals("Country", "Sweden", user.getCountry());&lt;br /&gt;   }&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-17677979981155746?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/17677979981155746/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=17677979981155746' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/17677979981155746'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/17677979981155746'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/01/example-driven-development-and-unit.html' title='Example Driven Development and Unit testing'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-2525373852364153300</id><published>2008-01-23T22:18:00.001+01:00</published><updated>2008-12-04T23:34:42.340+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JDBC'/><title type='text'>Database dump with Java</title><content type='html'>I need to update a database that is created by PHP. The problem is that I am not a PHP coder, but a Java coder, and I need to use some other Java libraries to get the job done. So how can find out exactly which tables to update and how? It would take me weeks to search the PHP code, and I still wouldn't be sure if I got it right.&lt;br /&gt;&lt;br /&gt;The first step is to install a clean application on my computer. There is no user data in the database, so if I perform commands like creating a user etc in the web application, I can look at what changed in the database. I'm sure that could be done in MySQL, but I'm not an expert on that either. When the only tool you have is a hammer, everything looks like a nail. So, I'll use Java for that to.&lt;br /&gt;&lt;br /&gt;So, I wrote a small Java application that produces exactly the output that I need. It reads metadata from the database to find all tables and columns, lists that metadata and the content of all the rows.&lt;br /&gt;&lt;br /&gt;Here it is:&lt;pre&gt;import java.io.FileNotFoundException;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.io.InputStream;&lt;br /&gt;import java.io.PrintWriter;&lt;br /&gt;import java.sql.Connection;&lt;br /&gt;import java.sql.DatabaseMetaData;&lt;br /&gt;import java.sql.DriverManager;&lt;br /&gt;import java.sql.PreparedStatement;&lt;br /&gt;import java.sql.ResultSet;&lt;br /&gt;import java.sql.ResultSetMetaData;&lt;br /&gt;import java.sql.SQLException;&lt;br /&gt;import java.text.SimpleDateFormat;&lt;br /&gt;import java.util.Date;&lt;br /&gt;import java.util.Properties;&lt;br /&gt;&lt;br /&gt;public class DumpDatabase {&lt;br /&gt;   private Connection conn;&lt;br /&gt;   private PrintWriter out;&lt;br /&gt;&lt;br /&gt;   public static void main(String[] args) {&lt;br /&gt;      try {&lt;br /&gt;         DumpDatabase app = new DumpDatabase();&lt;br /&gt;         String timestamp = new SimpleDateFormat("MMdd-hhmmss")&lt;br /&gt;.format(new Date());&lt;br /&gt;         String fileName = "dumpdatabase-" + timestamp + ".txt";&lt;br /&gt;         System.out.println("Creating " + fileName);&lt;br /&gt;         app.dumpDatabase(fileName);&lt;br /&gt;         System.out.println("Done.");&lt;br /&gt;      } catch(Exception e) {&lt;br /&gt;         e.printStackTrace();&lt;br /&gt;         System.exit(1);&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public DumpDatabase() throws IOException, &lt;br /&gt;ClassNotFoundException, SQLException {&lt;br /&gt;      // Get database connection from hibernate.properties.&lt;br /&gt;      // Or hard-code your own JDBC connection if desired.&lt;br /&gt;      InputStream in = getClass().getResourceAsStream(&lt;br /&gt;"/hibernate.properties");&lt;br /&gt;      Properties properties = new Properties();&lt;br /&gt;      properties.load(in);&lt;br /&gt;      String driver = properties.getProperty(&lt;br /&gt;"hibernate.connection.driver_class");&lt;br /&gt;      String url = properties.getProperty(&lt;br /&gt;"hibernate.connection.url");&lt;br /&gt;      String user = properties.getProperty(&lt;br /&gt;"hibernate.connection.username");&lt;br /&gt;      String password = properties.getProperty(&lt;br /&gt;"hibernate.connection.password");&lt;br /&gt;&lt;br /&gt;      Class.forName(driver);&lt;br /&gt;      conn = DriverManager.getConnection(url, user, password);&lt;br /&gt;   }&lt;br /&gt;   &lt;br /&gt;   public void dumpDatabase(String fileName) &lt;br /&gt;throws FileNotFoundException, SQLException {&lt;br /&gt;      out = new PrintWriter(fileName);&lt;br /&gt;      listAll();&lt;br /&gt;      out.close();&lt;br /&gt;      conn.close();&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public void listAll() throws SQLException {&lt;br /&gt;      DatabaseMetaData metadata = conn.getMetaData();&lt;br /&gt;      String[] types = { "TABLE" };&lt;br /&gt;      ResultSet rs = metadata.getTables(&lt;br /&gt;null, null, null, types);&lt;br /&gt;      while(rs.next()) {&lt;br /&gt;         String tableName = rs.getString("TABLE_NAME");&lt;br /&gt;         listTable(tableName);&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   private void listTable(String tableName) throws SQLException  {&lt;br /&gt;      PreparedStatement statement = conn.prepareStatement(&lt;br /&gt;            "select * from " + tableName + " a");&lt;br /&gt;      out.println("----" + tableName + "----");&lt;br /&gt;      int rowNo = 0;&lt;br /&gt;      ResultSet rs = statement.executeQuery();&lt;br /&gt;      while(rs.next()) {&lt;br /&gt;         if (rowNo == 0)&lt;br /&gt;            printTableColumns(rs);&lt;br /&gt;         printResultRow(rs);&lt;br /&gt;         rowNo++;&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   private void printTableColumns(ResultSet rs) &lt;br /&gt;throws SQLException {&lt;br /&gt;      ResultSetMetaData metaData = rs.getMetaData();&lt;br /&gt;      for(int i = 0; i &lt; metaData.getColumnCount(); i++) {&lt;br /&gt;         int col = i + 1;&lt;br /&gt;         out.println(metaData.getColumnName(col) + " "&lt;br /&gt;               + metaData.getColumnTypeName(col) + " " + "("&lt;br /&gt;               + metaData.getPrecision(col) + ")");&lt;br /&gt;      }&lt;br /&gt;      out.println("");&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   private void printResultRow(ResultSet rs) throws SQLException {&lt;br /&gt;      ResultSetMetaData metaData = rs.getMetaData();&lt;br /&gt;      for(int i = 0; i &lt; metaData.getColumnCount(); i++) {&lt;br /&gt;         String column = metaData.getColumnName(i + 1);&lt;br /&gt;         try {&lt;br /&gt;            String value = rs.getString(column);&lt;br /&gt;            if (value != null &amp;&amp; !value.equals("null") &lt;br /&gt;                  &amp;&amp; !value.equals("") &amp;&amp; !value.equals("0"))&lt;br /&gt;               out.print(column + ": " + value + ", ");&lt;br /&gt;         }&lt;br /&gt;         catch(SQLException e) {&lt;br /&gt;            out.print(column + ": " + e.getMessage());&lt;br /&gt;         }&lt;br /&gt;      }&lt;br /&gt;      out.println("");&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;The application reads the JDBC properties from an hibernate.properties file, for example like this:&lt;pre&gt;hibernate.dialect=org.hibernate.dialect.MySQL5Dialect&lt;br /&gt;hibernate.connection.driver_class=com.mysql.jdbc.Driver&lt;br /&gt;hibernate.connection.url=jdbc:mysql://localhost/users&lt;br /&gt;hibernate.connection.username=&amp;lt;username&amp;gt;&lt;br /&gt;hibernate.connection.password=&amp;lt;password&amp;gt;&lt;br /&gt;hibernate.show_sql=true&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It creates a file like dumpdatabase-01-23-10-30-38.txt:&lt;pre&gt;----tb_role----&lt;br /&gt;----tb_user----&lt;br /&gt;user_id BIGINT (20)&lt;br /&gt;username VARCHAR (255)&lt;br /&gt;password VARCHAR (255)&lt;br /&gt;first_name VARCHAR (255)&lt;br /&gt;last_name VARCHAR (255)&lt;br /&gt;email VARCHAR (255)&lt;br /&gt;&lt;br /&gt;----tb_user_role----&lt;/pre&gt;&lt;br /&gt;Then, I create a user and run the program again to get a new dump dumpdatabase-01-23-10-32-05.txt:&lt;pre&gt;----tb_role----&lt;br /&gt;----tb_user----&lt;br /&gt;user_id BIGINT (20)&lt;br /&gt;username VARCHAR (255)&lt;br /&gt;password VARCHAR (255)&lt;br /&gt;first_name VARCHAR (255)&lt;br /&gt;last_name VARCHAR (255)&lt;br /&gt;email VARCHAR (255)&lt;br /&gt;&lt;br /&gt;user_id: 1, username: albert, password: pw, first_name: albert, &lt;br /&gt;last_name: albertson, email: albert@dot.com, &lt;br /&gt;----tb_user_role----&lt;/pre&gt;&lt;br /&gt;Of course, in the real application the dumps are thousands of lines, but with Eclipse "Compare with each other", it' still easy to see what happened, so I know exactly what my Java code needs to do.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-2525373852364153300?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/2525373852364153300/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=2525373852364153300' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/2525373852364153300'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/2525373852364153300'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/01/integrating-with-phpmysql-application.html' title='Database dump with Java'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-5640164701821650650</id><published>2008-01-21T01:22:00.001+01:00</published><updated>2008-12-04T23:34:54.600+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Groovy'/><title type='text'>Refactor Groovy code?</title><content type='html'>I love Groovy! It's great for getting things done quickly. From Groovy, I can call Ant and XSLT easily. I can build and parse XML. I can call external applications, read file content and traverse a file hierarchy with very little code. And I can modify my program at runtime, without compiling and deploying it again.&lt;br /&gt;&lt;br /&gt;But this dynamics come at a cost. For instance, I really miss code completion. Some times, I go to a Java editor and create an object of some class and push CTRL-SPACE to find out the parameters for a method. Also, I miss that the editor doesn't indicate errors if I misspell a function or variable name.&lt;br /&gt;&lt;br /&gt;And I always wondered how well a dynamic language would scale. Now, I think I know the answer: It doesn't scale well! I have a Groovy template of 500 lines, and it's almost impossible to modify it. And today, I have been trying to refactor a Groovy class of 800 lines, and it's really, really hard.&lt;br /&gt;&lt;br /&gt;How hard would it be to modify a Java class of 500-800 lines? Not hard at all. Unfortunately, the Java version of these classes would probably be several thousand lines long, so there is no perfect solution that I know of. And I can probably blame myself for hacking these unmaintainable scripts together. But that's why I loved Groovy in the first place! The ability to produce something quickly.&lt;br /&gt;&lt;br /&gt;Actually, I write some code quickly in Java too some times, but I never have any problems refactoring it later. Maybe the problem is that there is no refactoring support in Groovy. I am so spoiled by Eclipse for Java in this area. I really hope this is the problem, and that refactoring support and auto completion comes to my editor soon. Because I love the productivity that Groovy gives.&lt;br /&gt;&lt;br /&gt;Until then, my advice is this: Use Groovy for relatively small scripts or to replace several other tools with one. For larger programs, I think it's better to stick with Java for now. Unless you know the domain, so you write code right the first time, and never need to refactor it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-5640164701821650650?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/5640164701821650650/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=5640164701821650650' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/5640164701821650650'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/5640164701821650650'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/01/refactor-groovy-code.html' title='Refactor Groovy code?'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-419356933817420294</id><published>2008-01-20T13:39:00.000+01:00</published><updated>2008-01-20T13:58:30.592+01:00</updated><title type='text'>Defensive Programming</title><content type='html'>After thinking some more about what I wrote in &lt;a href="http://larsho.blogspot.com/2008/01/small-java-trick-with-copy-paste.html"&gt;Small trick with Copy &amp; Paste&lt;/a&gt;, I saw that that's just an example of an attitude to programming. We can call it defensive programming.&lt;br /&gt;&lt;br /&gt;When I program, I always try to think "What can possibly go wrong here? And how can I make sure that doesn't happen?". One example is that I mess up variable names.&lt;br /&gt;&lt;br /&gt;Another example is that copy and paste are a source of problems. If I duplicate code, and I need to change it, I may forget to change it everywhere. So, it's usually better to generalize it.&lt;br /&gt;&lt;br /&gt;Another is that I change how a method is supposed to be used. For example, let's say I have a function: &lt;pre&gt;    void sendEmail(String to, String cc,&lt;br /&gt;                   String subject, String body)&lt;/pre&gt;But then I change it to &lt;pre&gt;    void sendEmail(String to, String subject,&lt;br /&gt;                   String body, String attachment)&lt;/pre&gt;So all callers have to change their parameters for this to work properly.&lt;br /&gt;&lt;br /&gt;To be sure this doesn't break, it's better to rename the method: &lt;pre&gt;    void sendEmailWithAttacment(String to, String subject,&lt;br /&gt;                                String body, String attachment)&lt;/pre&gt;Then, the compiler will make sure it works. At least in a static language like Java. In Groovy or something you wouldn't detect this until runtime. That's why I'm not totally sold into dynamic languages, although I  like Groovy a lot. If we use a dynamic language, we have to be defensive by using more unit tests.&lt;br /&gt;&lt;br /&gt;A final example is if you fix a bug. There is always a risk that you or someone else will revert the change. How can we be sure this doesn't happen? The best way is to reproduce the bug with a unit test before fixing it. If this is too much work, at least write a comment explaining the fix.&lt;br /&gt;&lt;br /&gt;It all comes down to admitting that I do mistakes and trying to prevent them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-419356933817420294?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/419356933817420294/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=419356933817420294' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/419356933817420294'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/419356933817420294'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/01/defensive-programming.html' title='Defensive Programming'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-5453416185913696628</id><published>2008-01-18T23:26:00.001+01:00</published><updated>2011-02-10T22:43:34.610+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='requirements'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Example Driven Development</title><content type='html'>This article is the first in a series about Test Driven Development. A test is an example of what a program should do. Examples make the requirements concrete, so they become easier to understand. I will show how examples can be used for requirements, manual testing and unit testing.&lt;br /&gt;&lt;br /&gt;I am currently working on automatic order handling for a client. I will implement a servlet with the following tasks:&lt;br /&gt;1. Decode input parameters from a web page.&lt;br /&gt;2. Generate an encrypted license with some of those parameters.&lt;br /&gt;3. Store the license in a database. I can model this database as I want.&lt;br /&gt;4. Store customer data in another database. This database schema is "carved in stone".&lt;br /&gt;5. Send the license as an email to the user.&lt;br /&gt;&lt;br /&gt;This is a small project, where I will implement, test and deploy the servlet myself. If it doesn't work, my client will lose customers, and I will be responsible.&lt;br /&gt;&lt;br /&gt;So how can I make sure this doesn't happen?&lt;br /&gt;&lt;br /&gt;I don't trust myself to make so good quality that there won't be any problems. So, I need to do thorough testing. I will make unit tests, but how do I make good ones?&lt;br /&gt;&lt;br /&gt;Unit testing is important, but it's not enough. There are always some issues that don't show up until manual testing. I also need to test the integration between my program and other components manually. But how do I make good manual tests? Do I need to write a formal test specification for this simple program?&lt;br /&gt;&lt;br /&gt;I think I do. I need to discuss the requirements with my client, and some examples of what the program is supposed to do is the best way to do that. By making a few concrete examples, I can make sure there are no misunderstandings, and that no requirements are missing. I'm sure there will be some misunderstandings anyway, but that's why I'm going to test it.&lt;br /&gt;&lt;br /&gt;I'll use this test specification both for manual testing and as basis for the unit tests. That way, I'll have good unit tests that cover the functionality well, and there hopefully shouldn't bee too many surprises when it comes to integration testing.&lt;br /&gt;&lt;br /&gt;I start by writing down 2 examples that the servlet should handle. For each example, I will list the input parameters, output values and any changes to the databases. It doesn't take long to write these down on a piece of paper, and it will be very much worth the effort.&lt;br /&gt;&lt;br /&gt;&lt;font size="4"&gt;Example 1: A customer orders 1 license of type "Enterprise"&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;font style="font-weight: bold;"&gt;Input&lt;/font&gt;&lt;pre&gt;product_id   = (ask the client what the product id is)&lt;br /&gt;quantity     = 1&lt;br /&gt;company_name = Dot Com Inc&lt;br /&gt;email        = john@dotcom.com&lt;/pre&gt;I have already identified a very important question I need to ask my client: What are the product ids that the servlet will receive?&lt;br /&gt;&lt;br /&gt;&lt;font style="font-weight: bold;"&gt;License Content&lt;/font&gt;&lt;pre&gt;type         = Enterprise&lt;br /&gt;company name = Dot Com Inc&lt;br /&gt;email        = john@dotcom.com&lt;br /&gt;expires      = 3/25/2009&lt;/pre&gt;&lt;font style="font-weight: bold;"&gt;License Database&lt;/font&gt;&lt;br /&gt;Insert a license with the following values:&lt;pre&gt;type         = Enterprise&lt;br /&gt;company name = Dot Com Inc&lt;br /&gt;email        = john@dotcom.com&lt;br /&gt;date         = 3/25/2008&lt;br /&gt;expires      = 3/25/2009&lt;/pre&gt;&lt;font style="font-weight: bold;"&gt;Customer Database&lt;/font&gt;&lt;br /&gt;(I need to ask the client about this.)&lt;br /&gt;&lt;br /&gt;&lt;font style="font-weight: bold;"&gt;Email&lt;/font&gt;&lt;pre&gt;to         = john@dotcom.com&lt;br /&gt;subject    = License&lt;br /&gt;content    = Please find attached an Enterprise license to&lt;br /&gt;             Dot Com Inc and email john@dotcom.com.&lt;br /&gt;attachment = enterprise.lic&lt;/pre&gt;&lt;font style="font-weight: bold;" size="4"&gt;Example 2: A customer orders 1 license of type "Basic"&lt;br /&gt;&lt;br /&gt;&lt;/font&gt;&lt;font style="font-weight: bold;"&gt;Input&lt;/font&gt;&lt;pre&gt;product_id   = (Ask the client)&lt;br /&gt;quantity     = 1&lt;br /&gt;company_name = Dot Com Inc&lt;br /&gt;email        = john@dotcom.com&lt;/pre&gt;&lt;font style="font-weight: bold;"&gt;License Content&lt;/font&gt;&lt;pre&gt;type         = Basic&lt;br /&gt;company name = Dot Com Inc&lt;br /&gt;email        = john@dotcom.com&lt;br /&gt;expires      = 3/25/2009&lt;/pre&gt;&lt;font style="font-weight: bold;"&gt;License Database&lt;/font&gt;&lt;br /&gt;Insert a license with the following values:&lt;pre&gt;type         = Basic&lt;br /&gt;company name = Dot Com Inc&lt;br /&gt;email        = john@dotcom.com&lt;br /&gt;date         = 3/25/2008&lt;br /&gt;expires      = 3/25/2009&lt;/pre&gt;&lt;font style="font-weight: bold;"&gt;Customer Database&lt;/font&gt;&lt;br /&gt;(Ask the client.)&lt;br /&gt;&lt;br /&gt;&lt;font style="font-weight: bold;"&gt;Email&lt;/font&gt;&lt;pre&gt;to           = john@dotcom.com&lt;br /&gt;subject      = License&lt;br /&gt;content      = Please find attached a Basic license to&lt;br /&gt;               Dot Com Inc and email john@dotcom.com.&lt;br /&gt;attachment   = basic.lic&lt;/pre&gt;Now, I need to go through these examples with my client.&lt;br /&gt;&lt;br /&gt;When I talked to the client, the he pointed out that it is possible to order multiple licenses of the "Enterprise" product at once. I hadn't though about that. Together, we discussed how to handle this.&lt;br /&gt;&lt;br /&gt;In the next article, I will write about how to implement and unit test the servlet.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-5453416185913696628?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/5453416185913696628/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=5453416185913696628' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/5453416185913696628'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/5453416185913696628'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/01/example-driven-development.html' title='Example Driven Development'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-5386427311317195506</id><published>2008-01-18T12:54:00.000+01:00</published><updated>2008-01-18T13:04:21.449+01:00</updated><title type='text'>Small Java trick with copy - paste</title><content type='html'>Here is a small trick I use to avoid getting into problems.&lt;br /&gt;&lt;br /&gt;Some times, I need to copy a block with a variable, for instance:&lt;pre&gt;      Address addr = list.get(0);&lt;br /&gt;      assertEquals("city", "Uppsala", addr.getCity());&lt;/pre&gt;and paste it to create something similar like this:&lt;pre&gt;      Address addr2 = person.getAddress()&lt;br /&gt;      assertEquals("city", "Uppsala", addr.getCity());&lt;/pre&gt;Did you see the bug?&lt;br /&gt;&lt;br /&gt;The trick I use to avoid bugs like this, is to change the variable name in the original block:&lt;pre&gt;      Address addr1 = list.get(0);&lt;br /&gt;      assertEquals("city", "Uppsala", addr1.getCity());&lt;br /&gt;      Address addr2 = person.getAddress()&lt;br /&gt;      assertEquals("city", "Uppsala", addr.getCity());&lt;/pre&gt;Then the bug becomes obvious.&lt;br /&gt;&lt;br /&gt;I can't tell you how many times this has saved me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-5386427311317195506?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/5386427311317195506/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=5386427311317195506' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/5386427311317195506'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/5386427311317195506'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/01/small-java-trick-with-copy-paste.html' title='Small Java trick with copy - paste'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-3181641279966604648</id><published>2008-01-12T00:55:00.001+01:00</published><updated>2008-12-04T23:35:16.606+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Robustness analysis'/><category scheme='http://www.blogger.com/atom/ns#' term='Use cases'/><title type='text'>Use cases and robustness analysis</title><content type='html'>Use cases or user stories? Use cases are bigger and require more work. User stories are quicker and easier, more &lt;i&gt;agile&lt;/i&gt;. That may be ok if you have other agile practices in place. If you have a customer or functional expert available to the team at all times, user stories may work quite well. But if you don't, you may need something heavier, like use cases.&lt;br /&gt;&lt;br /&gt;It also depends on the complexity of the requirements. If the requirements are simple, use cases are not necessary. But for complex requirements, I think use cases are better suited than user stories. They help the customer to think through the requirements earlier, and they give the developers better understanding of what the system is supposed to do.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;But use cases are not agile!&lt;/i&gt; Yes, they may be. Agile doesn't mean lightweight, it means &lt;i&gt;right&lt;/i&gt;weight. It means you adapt the process to the project. A large, complex project needs a heavier process than small and simple ones.&lt;br /&gt;&lt;br /&gt;RUP had an activity called &lt;i&gt;robustness analysis&lt;/i&gt; to analyze model, view and controller objects from use cases. I never really understood the purpose of this, until recently I read a book called &lt;i&gt;Use Case Driven Object Modeling with UML&lt;/i&gt;. I strongly recommend this book for everyone using use cases and/or object oriented analysis. It argues against many popular opinions about use cases that they should be abstract and not deal with the user interface. These kind of abstract use cases don't give the developers the information they need, leaving them to make their own assumptions about how the system should work.&lt;br /&gt;&lt;br /&gt;This doesn't mean that the use cases should include user interface design, but some specification of the user interface is necessary. Not only is this necessary to keep the developers on track, it also helps to make the use cases better.&lt;br /&gt;&lt;br /&gt;The book explains how drive the object design from the use cases, resulting in a good object model that is consistent with the requirements.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-3181641279966604648?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/3181641279966604648/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=3181641279966604648' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/3181641279966604648'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/3181641279966604648'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/01/use-cases-and-robustness-analysis.html' title='Use cases and robustness analysis'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9139942395882504841.post-8010765906729054971</id><published>2008-01-07T10:17:00.001+01:00</published><updated>2008-12-04T23:39:58.634+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='complexity'/><category scheme='http://www.blogger.com/atom/ns#' term='communication'/><title type='text'>Why Agile Methods Reduce Cost</title><content type='html'>The greatest cost of software development is complexity:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The problems we are trying to solve are complex. The customers often don't understand the requirements completely before the software development starts.&lt;/li&gt;&lt;li&gt;The mapping between the requirements and the software is also complex, so that misunderstandings between the customers and the developers are the rule and not exceptions.&lt;/li&gt;&lt;li&gt;The software itself is complex. It is hard for the developers to get an overview of what it does, and the consequences of modifying the software are hard to predict.&lt;/li&gt;&lt;/ol&gt;This complexity adds risk to software development projects, so it is actually uncommon for software projects to complete in time and on budget. Complexity also results in poor quality, since errors occur all the way in the development process; in the requirements, while mapping the requirements to the implementation, and in the implementation.&lt;br /&gt;&lt;br /&gt;The risk and the low quality obviously increases the development cost. The cost is also increased by the the need for more developers and experts who know how to handle the complexity, and the need for substantial testing to find and fix errors.&lt;br /&gt;&lt;br /&gt;So how can we deal with this complexity?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Requirements&lt;/span&gt;&lt;br /&gt;We cannot reduce the complexity of the requirements, but I beleive a good domain model is important to understand complex requirements. Make the model concrete by using examples. Model the object instances for a use case instance, and go through the variations and changes that can happen with the customer.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Communication&lt;/span&gt;&lt;br /&gt;The number one success factor in development projects is communication. We need to improve communication between customers and developers, and within the development team.&lt;br /&gt;&lt;br /&gt;Agile methods does this through a number of key practices:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Iterative development gives rapid feedback on misunderstandings between customers and developers.&lt;/li&gt;&lt;li&gt;Ideally, a customer should be available to the developers all the time. &lt;/li&gt;&lt;li&gt;Daily stand-up meetings improve the communication within the team.&lt;/li&gt;&lt;li&gt;Pair programming.&lt;/li&gt;&lt;/ul&gt;Also, the domain model is very valuable as a means for communicating within the team.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Software Complexity&lt;/span&gt;&lt;br /&gt;By complexity of software I mean how many thoughts the developer has to hold in his head at once. Early Java frameworks separated the code into lots of losely connected classes and XML files. The purpose was reuse and easy configuration, but it nevertheless made the program fragmented and hard to understand.&lt;br /&gt;&lt;br /&gt;I personally hate it when I have to jump from a Java class to struts-config.xml to find the name of a form Java class. I just want to push F3. I find it ironic that software developers that are designing user interfaces for a living can come up with something as awkard as XML configuration files.&lt;br /&gt;&lt;br /&gt;It is said that the human mind can hold between 5 and 9 pieces of information at once. If you need to hold the 1 action class, 1 form, 1 DAO, 1 JSP page, 2 XML files and 1 Javascript file, that doesn't leave much space for the feature you are trying to develop.&lt;br /&gt;&lt;br /&gt;Luckily, this situation is beeing addressed by a new generation of programming languages and frameworks such as Ruby on Rails.&lt;br /&gt;&lt;br /&gt;I beleive the cost of switching to a simpler framework will pay back several times by the improved speed that comes by being able to focus on what you are actually trying to do.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9139942395882504841-8010765906729054971?l=larsho.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://larsho.blogspot.com/feeds/8010765906729054971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9139942395882504841&amp;postID=8010765906729054971' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/8010765906729054971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9139942395882504841/posts/default/8010765906729054971'/><link rel='alternate' type='text/html' href='http://larsho.blogspot.com/2008/01/why-agile-methods-reduce-cost.html' title='Why Agile Methods Reduce Cost'/><author><name>Lars</name><uri>http://www.blogger.com/profile/00678755029500688039</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry></feed>
