David's Blog

Scrum... But?

"We are supposed to do scrum, but we don't have (a full time ScrumMaster | a material task board | an electronic task board | management support | short sprints | long enough sprints | magic dust | ...) so we are really only doing ScrumBut. It sucks. Everything would be better if we could only do *real* Scrum".

Did you ever hear something like that? I have heard it a couple of times, and complaints like this seem to increase. At first I thought the idea of "ScrumBut" was funny and useful, but I have begun to dislike it.

Complaining about not being able to fully execute any given process is not what a software development team should do. Developing software is not about the process. It is not about Scrum vs. ScrumBut vs. Waterfall.

What is important, at the end of the day (week, sprint), is delivering. Providing value for our customers. Working software. And it is equally important to have some fun while doing it. Because I don't want to burn out. And because I don't want my other team mebers to burn out (or anybody, for that matter). Complaining does not help with having more fun.

Scrum has helped a lot of teams to achieve what is important. So there's a good chance that it can help your team too. But it's not the only solution. And scrum is not important for it's own sake. It is important because it helps organizations to delvier value.

Dreaming how everything would be better if we could *just* work a little bit different does not help either. Suppose my team's performance sucks, and we start to blame it on not having a full time ScrumMaster. Do we really expect *everything* to be fine if we had one?

Don't get me wrong. I know that the ScrumMaster is important for the success of a team. And I know that it is a full time job. And I know that short iterations, a task board and a lot of other things are important. But complaining or dreaming won't help.

What might help is trying to change the organization your team has to work in. You can convince your managers that your team needs a full time ScrumMaster. You can start to educate people in you organization about scrum. And this will (probably) work. But only in the long run.

But what can you do *now*? Inspect and adapt. Inspect: "We don't have a full time ScrumMaster". Adapt: Find solutions that make living with this situation easier. As a team. One step at a time. Do retrospectives. Change some little thing, every week. And: Don't forget what you have accomplished. Try to see what is already working. Celebrate the victories, even the little ones.

Don't complain or dream, act.

Update: I wrote a follow-up called ScrumBut... and the long run.

You might be also interested in:

Learn more about how I can help you save money and earn money by improving your agile software development process.

Posting Type: 

Offline Web Applications - Part 4: Going Offline

This mini series about "Offline Web Applications" shows you how to build a web application that your users can use while they are offline. I will give a session about this topic at Herbstcampus 2012.

What is still missing?

The dictionary can not be used offline at the moment. We want to change that. We will load the dictionary content from the server into a client side database. Then users of the dictionary can access the data offline. We will not allow editing the dictionary offline.

The client side database we use is IndexedDB, together with the JQuery IndexedDB plugin.

The indexeddb plugin

We have to add the JQuery IndexedDB plugin to our HTML and offline manifest:

dictionary.html

<head>
    <!-- ... -->
    <script src="jquery-1.8.0.min.js"></script>
    <script src="jquery.indexeddb.js"></script>
    <!-- ... -->
</head>

offline.manifest

CACHE MANIFEST
# Offline cache manifest for the dictionary application
# version 2

CACHE:
dictionary.html
style.css
jquery-1.8.0.min.js
jquery.indexeddb.js
dictionary.js

NETWORK:
api/listEntries
api/newEntry

The complete application

We have to change the HTML page slightly: When the document is loaded, we have to do more than just loading the dictionary content. So we change this function call to "initialize". Also, the function that submits the data needs the values of the two input fields, so it can store them to the IndexedDB.

dictionary.html

<head>
    <!-- ... -->
		
    <script>
        $(document).ready(function() {
            initializeDictionaryApplication();
            $('#submitButton').click(function() {
                submitDictionaryEntry($('#addForm'),
                $('input[name="german"]').val(), 
                $('input[name="english"]').val());
                return false;
            });
        });
    </script>
</head>

The JavaScript code has to be changed significantly. To initialize the application, we first have to initialize the offline database. This is an asynchronous invocation, but we can add a callback function which is notified when the initialization is done. Then we can load the server content.

dictionary.js

initializeDictionaryApplication = function() {
    initializeOfflineDatabase().done(function() {
        loadDictionaryContent();
    });
};

The schema that is required to initialize the database is a mapping of version numbers to functions. Each function describes how the schema has to be changed to get from the previous version to the current. This initialization routine creates a database called "dictionary". The database contains a single object store called "Words", where we will store "Word" objects.

function Word(word, translation) {
    this.word = word;
    this.translation = translation;
}

initializeOfflineDatabase = function() {
    return $.indexedDB("dictionary", {
        "schema" : {
            "1" : function(transaction) {
                var dictionary = transaction.createObjectStore("words", {
                    "keyPath" : "word"
                });
                dictionary.createIndex("translation");
            }
        }
    });
};

Loading the database works similar as before, but instead of showing the data directly we save it to the database. Then we show the database content. When an error occurs (i.e. when the user is offline) we just show the database content, which still contains all the words that were loaded the last time the user was online.

loadDictionaryContent = function() {
    $.ajax({
        url:'api/listEntries'
    }).done(function(data) {
        $.indexedDB("dictionary").objectStore("words").clear();

        var dataArray = JSON.parse(data);
        var words = $.indexedDB("dictionary").objectStore("words");
        
        $.each(dataArray, function(index, value) {
            words.add(new Word(value.german, value.english));
        });
        
        showDatabaseEntries();
    }).error(function() {
        showDatabaseContent();
    });
};

Showing the database content just iterates over all entries in the object store and creates the corresponding HTML.

showDatabaseEntries = function() {
    $("#wordsList").empty();
    $.indexedDB("dictionary").objectStore("words").each(function(item) {
        $("#wordsList").append('<div class="left">' + item.value.word + '</div>');
        $("#wordsList").append('<div class="right">' + item.value.translation + '</div>');
        $("#wordsList").append('<div class="clear"></div>');
    });
};

When a new word is submitted, it is not only sent to the server, we also store it in the database. Note that for submitting words, we still need an online connection. We could make this feature available offline too, but then we would need a synchronization mechanism. This is left as an exercise to the reader :)

submitDictionaryEntry = function(form, german, translation) {
    $.indexedDB("dictionary").objectStore("words")
            .add(new Word(german, translation))
            .done(function(result, event) {
        sendEntryToServer(form.serialize());
        showDatabaseEntries();
    });
};

sendEntryToServer = function(formContent) {
    $.ajax({
        url:'api/newEntry',
        type: 'GET',
        data: formContent
    });
};

Conclusion

Storing data offline has become really easy for web applications, but it is not yet supported by all browsers. In the not-so-far future this will be an interesting alternative to native apps.

Posting Type: 

The future is agile

The future is agile. Companies and software developers can gain many advantages by adopting an agile approach. Here I want to describe two of them: Speed and talent. You can go faster using an agile approach. And you can attract more and better developers.

This post is based on my Herbstcampus 2012 keynote. Here is the video of the (german) lightning talk:

Speed

An important measure for speed in a software project is "cycle time". Cycle time is the time it takes you from having an idea to delivering the finished, usable product to your customers. Shorter cycle times mean that you go faster. And going fast is important: We live in a world of constant change. And change accelerates. Going fast means that you can react to those changes.

Most of the software developed today is written for the web - There are even graphics programs and 3D games available as web applications! And the percentage will rise. Developing for the web has just too many advantages over developing native apps. You can, for example, measure in real time how your users use the application. You can even add offline capabilities.

And you can deliver a new version of your software at any time - multiple times a day! This makes cycle times of hours or days possible. But in most organizations, their processes and policies make daily delivery - or continuous deployment - outright impossible. They need radically different processes and policies before they can even think of continuous delivery.

Most organizations today have cycle times measured in months or years, not hours or days. This is a relic from a past where delivering faster was not possible. And for now, most of these organizations will do just fine. Because all their competitors are just as slow. But the clock is ticking. A competitor might find a way to go faster... Continuous delivery, when implemented now, can delight your customers. But, at some point in the future, it may be necessary just to survive!

Talent

The problem with software developers is that they are becoming scarcer. Not that our number decreases. It's just that the demand rises faster than schools can educate new developers.

The demand rises because more and more companies from diverse industries start to develop software. They have to, just to stay in business or to gain a competitive advantage. "The software industry" does not exist anymore. Every industry is a software industry.

When you are looking for developers right now this leaves you in an awkward position. I have heard of companies who don't get even one application per open job over a period of months! This means they either have to accept every applicant or work with less developers. This is not a situation you want to be in. What does your company offer to attract talent?

Money will not necessary help. Everybody offers money. And after reaching a certain threshold, money is not the top priority for most people. Other thing suddenly become more important. Things like responsibility and accountability: People want to be responsible for something and accountable for the results. This might sound scary at first, but it is actually a good position to be in.

People want to be trusted. And they want to be able to trust others. To trust their management, their company, their coworkers. People want to be proud of what they did, proud of what they were part of. And they want to work on interesting projects and have an impact on the outcome of these projects.

Conclustion

A team of good developers, working in an agile way, can work at the speed described above, giving you the advantage of being fast. And you can offer your developers more than money. You can offer them what most people seek in their lives.

Do you want me to talk about this or a similar topic at your comany, user group meeting, conference or event? Do you have any questions, or points where you disagree with me? You can find all the ways to contact me at the bottom of this page. Please use them :)

You might be also interested in...
Learn more about how I can help you save money and earn money by improving your implementation of agile software development.

Posting Type: 

Offline Web Applications - Part 3: Application Logic

This mini series about "Offline Web Applications" shows you how to build a web application that your users can use while they are offline. I will give a session about this topic at Herbstcampus 2012.

JQuery, document.ready

We start by including some JavaScript in the dictionary page. The submit button needs an ID. We will implement the functions "loadDictionaryContent" and "submitDictionaryEntry" in the file "dicitionary.js".

dictionary.html

<head>
    <!-- ... -->
    <script src="jquery-1.8.0.min.js"></script>
    <script src="dictionary.js"></script>
		
    <script>
        $(document).ready(function() {
            loadDictionaryContent();
            $('#submitButton').click(function() {
                submitDictionaryEntry($('#addForm').serialize());
                return false;
            });
        });
    </script>
</head>
<body>
    <!-- ... -->
                     <input type="submit" value="Add" id="submitButton"/>
    <!-- ... -->
</body>

dictionary.js

The JavaScript file implements the two functions "loadDictionaryContent" and "submitDictionaryEntry". These use AJAX to call the backend to load the dictionary content and to save new words.

loadDictionaryContent = function() {
    $.ajax({
        url:'api/listEntries'
    }).done(function(data) {
        $(wordsList).empty();
        
        var dataArray = JSON.parse(data);
        $.each(dataArray, function(index, value) {
            $(wordsList).append('<div class="left">' + value.german + '</div>');
            $(wordsList).append('<div class="right">' + value.austrian + '</div>');
            $(wordsList).append('<div class="clear"></div>');
        });
    });
};

submitDictionaryEntry = function(formContent) {
    $.ajax({
        url:'api/newEntry',
        type: 'GET',
        data: formContent
    }).done(function(data) {
        loadDictionaryContent();
    });
};

API

We have to create the two API endpoints "api/listEntries" and "api/newEntry" in the backend. I use wicket pages to implement them. In a real project you should probably use a REST library.

ListEntries.java

public class ListEntries extends Page {
    private static final long serialVersionUID = 1L;
    
    @Inject private DictionaryService dictionaryService;
    
    public ListEntries() {
    }
    
    @Override
    public void renderPage() {
        List<DictionaryWordEntity> entries = dictionaryService.listAllEntries();
        String entriesJson = new Gson().toJson(entries);
        
        PrintWriter w = new PrintWriter(getResponse().getOutputStream());
        w.println(entriesJson);
        w.close();
    }
}

NewEntry.java

public class NewEntry extends Page {
    private static final long serialVersionUID = 1L;
    
    @Inject private DictionaryService dictionaryService;

    public NewEntry(PageParameters params) {
        String german = params.get("german").toString("");
        String austrian = params.get("austrian").toString("");

        dictionaryService.addWord(german, austrian);
    }
    
    @Override
    public void renderPage() {
    }
}

DictionaryApplication.java

public class DictionaryApplication extends WebApplication {
    @Override
    protected void init() {
        //...
        mountPage("api/listEntries", ListEntries.class);
        mountPage("api/newEntry", NewEntry.class);
    }
}

Cache Manifest

We have to update the cache manifest to include the 2 new javascript files. We also have to add the 2 API endpoints to the "NETWORK" section so the browser knows to request them online. You might notice that the browser does not update any "CACHE"d resources when the cache manifest is unchanged. To be able to control when the browser updates these resources we add a "version" comment that we increment every time a resource has changed. You should increment this version automatically during your build or deployment process in a real project. For this example manually incrementing the version number is good enough.

offline.manifest

CACHE MANIFEST
# Offline cache manifest for the dictionary application
# version 1

CACHE:
dictionary.html
style.css
jquery-1.8.0.min.js
dictionary.js

NETWORK:
api/listEntries
api/newEntry

Coming up next...

And that's it: The dictionary application works again! And it is still available offline. But it still does not work in offline mode. For this we need to load all data into an offline data base. Stay tuned for part 4 where we will finish the application and make everything available offline!

Posting Type: 

Offline Web Applications - Part 2: Application Cache

This mini series about "Offline Web Applications" shows you how to build a web application that your users can use while they are offline. I will give a session about this topic at Herbstcampus 2012.

No server side components

In the first example we used several server side components: A page class (written in Java) with buttons and text fields. This class was also responsible for loading/storing data. Because we want to implement offline mode, we can not use these server side components. We have to render a simple HTML page. This page is empty right now, we just want to have it available offline. We will implement the new dictionary functionality in part 3 of this series.

I have moved the file "style.css" to the "WebContent" folder of my web application. In this folder I also added a file called "dictionary.html":

dictionary.html

<!DOCTYPE html>

<html manifest="offline.manifest">
    <head>
        <link rel="stylesheet" type="text/css" href="style.css"/>
    </head>
    <body>
        <div class="content">
            <h1>German - English dictionary</h1>
            
            <div class="block grey">
                <h2>Add word</h2>
                
                <form id="addForm">
                    <div class="left">
                        <h3>German</h3>
                        <input type="text" id="german"/>
                    </div>
                    <div class="right">
                        <h3>English</h3>
                        <input type="text" id="english"/>
                    </div>
                    <div class="clear"></div>
                    <input type="submit" value="Add"/>
                </form>
            </div>
            
            <div class="block green">
                <h2>Words</h2>
                
                <div class="left">
                    <h3>German</h3>
                </div>
                <div class="right">
                    <h3>English</h3>
                </div>
                <div class="clear"></div>
                
                <div id="wordsList">
                    <div class="left" id="listEntryGerman">German word</div>
                    <div class="right" id="listEntryEnglish">English word</div>
                    <div class="clear"></div>
                </div>
            </div>
        </div>
    </body>
</html>

Wicket

We don't need the DictionaryPage anymore, so I deleted the java class and the HTML file. I have also removed the home page form the application class:

DictionaryApplication.java

@Override
public Class extends Page> getHomePage() {
    return null;
}

Cache-Manifest for offline mode

Notice that the "html" tag has a "manifest" attribute? We need this manifest to make resources available offline. So let's add the file offline.manifest to the folder "WebContent":

offline.manifest

CACHE MANIFEST
# Offline cache manifest for the dictionary application

CACHE:
dictionary.html
style.css

And that's it: Our dictionary page is available offline! Just... it doesn't work anymore, because all the logic was on the server side, encoded in a wicket page. Stay tuned for part 3, where we'll implement the new logic for creating and displaying translations!

Posting Type: 

Kitchen knives (and other tools)

Once you get used to a great tool you don't want to use anything else. But people who only use mediocre tools don't even know that they need something better. Or even worse: They don't like the better tool at first. Let me give you some examples... (This blog posting is somewhat related to Cheap plastic drills).

Santoku knive, chili

Kitchen Knives

I love to cook. And I think that great tools can make a difference. So, a couple of years ago, I bought two rather expensive kitchen knives. I loved them right away, but it took some time to really get used to them. And it took some more time practicing to sharpen them with a grindstone (A knife is sharp when you can use it to shave the back of your hand).

Now that I am used to having sharp knives I just can't stand working with "normal" ones. I rarely use my cheap knives anymore - only for cutting cheese, butter and other "soft" stuff. But when a friend of mine, who likes cooking too, tried my knives he said: "I don't know, they don't really feel right. And I don't need such a sharp knife anyway". Not only was he satisfied with mediocre tools, he didn't even like the better tools at first!

Nakiri knive and ingredients for sambal oelek

Software tools

I have seen something similar with software tools too. I come from a Java background. I have been using Eclipse and Mercurial for some time. When I started my first .NET project I was shocked how many shortcomings Visual Studio and Team Foundation Server (both version 2010) had compared to what I was used to. It is impossible to operate Visual Studio only with the keyboard. Refactoring is a Joke. You can not search for types. TFS sometimes loses local edits, and offline mode and merging are a pain. And don't get me started about MSTest. Just to mention a few of my issues...

But my coworkers, who had used both tools for several years, didn't understand my criticism. They thought that those were minor issues and that one didn't need those features anyway. They told me that VS and TFS were excellent products, and that all tools have shortcomings. Well, I can not agree with the first part of that sentence.

I am guilty too

Cutting chili

I don't sharpen my my knives often enough. I still use Java for most of my programming. I kinda like Ruby and Python but never learned them well enough so they could replace Java for me. I want to learn Clojure and Light Table but I don't find the time. I still use Eclipse although I know that IntelliJ has some features I'd probably like.

So, yes: I am guilty too. But I hope to improve one or another of the above in the coming months. What about you?

You might be also interested in:

Posting Type: 

Offline Web Applications - Part 1: The basic application

This mini series about "Offline Web Applications" shows you how to build a web application that your users can use while they are offline. I will give a session about this topic at Herbstcampus 2012.

I have decided to create a simple dictionary web application using wicket. This application allows you to enter a German word and its English translation. It lists all the translations it already knows.

Screenshot Dictionary App

The rest of this blog posting will show you the whole source code of this application. So, there will be mostly code. This can - frankly - be a little bit boring. But the other parts of this mini series will gradually change this code to make the application available offline, and I think this code is necessary to understand the future steps. So, here it is.

DictionaryPage.html

Let's start with what you see when you start the application: The dictionary page. Notice that it uses a stylesheet which has to be made available for offline use later. Otherwise there is nothing too fancy about this page.

<html>
    <head>
        <wicket:link>
            <link rel="stylesheet" type="text/css" href="style.css"/>
        </wicket:link>
    </head>
    <body>
        <div class="content">
            <h1>German - English dictionary</h1>
            
            <div class="block grey">
                <h2>Add word</h2>
                
                <form wicket:id="addForm">
                    <div class="left">
                        <h3>German</h3>
                        <input type="text" wicket:id="german"/>
                    </div>
                    <div class="right">
                        <h3>English</h3>
                        <input type="text" wicket:id="english"/>
                    </div>
                    <div class="clear"/>
                    <input type="submit" value="Add"/>
                </form>
            </div>
            
            <div class="block green">
                <h2>Words</h2>
                
                <div class="left"><h3>German</h3></div>
                <div class="right"><h3>English</h3></div>
                <div class="clear"/>
                
                <div wicket:id="wordsList">
                    <div class="left" wicket:id="listEntryGerman"/>
                    <div class="right" wicket:id="listEntryEnglish"/>
                    <div class="clear"/>
                </div>
            </div>
        </div>
    </body>
</html>

DictionaryPage.java

There is a java class backing the HTML view above. All it does is wire up the components. It redirects everything domain-related to a service.

public class DictionaryPage extends WebPage {
    private static final long serialVersionUID = 1L;

    @Inject private DictionaryService dictService;
    
    private String germanWord;
    private String englishWord;
    
    public DictionaryPage() {
        Form<Void> addForm = new Form<Void>("addForm") {
            private static final long serialVersionUID = 1L;
            
            @Override
            protected void onSubmit() {
                dictService.addWord(germanWord, englishWord);
                germanWord = null;
                englishWord = null;
            }
        };
        add(addForm);
        
        TextField<String> german = new TextField<>("german", 
            new PropertyModel<String>(this, "germanWord"));
        addForm.add(german);
        
        TextField<String> english = new TextField<>("english", 
            new PropertyModel<String>(this, "englishWord"));
        addForm.add(english);
        
        DictionaryDataProvider dictionaryDataProvider = dictService.getDataProvider();
        DataView<DictionaryWordEntity> wordsList = new 
            DataView<DictionaryWordEntity>("wordsList", dictionaryDataProvider) {
            private static final long serialVersionUID = 1L;

            @Override
            protected void populateItem(final Item<DictionaryWordEntity> item) {
                item.add(new Label("listEntryGerman", item.getModelObject().getGerman()));
                item.add(new Label("listEntryEnglish", 
                    item.getModelObject().getEnglish()));
            }
        };
        add(wordsList);
    }
}

Wicket Application and Guice Module

The wicket application and the guice module (which configures the injector and allows us to @Inject dependencies) comple the wicket related stuff. The file "web.xml", which has to set up the wicket filter and the application, is not shown here.

ltpublic class DictionaryApplication extends WebApplication {
    private GuiceComponentInjector injector;

    @Override
    protected void init() {
        super.init();
        
        injector = new GuiceComponentInjector(this, new 
            DictionaryModule(getServletContext().getRealPath("dictionary.database")));
        getComponentInstantiationListeners().add(injector);
    }
    
    @Override
    public Class<? extends Page> getHomePage() {
        return DictionaryPage.class;
    }
}

public class DictionaryModule implements Module {
    private String databasePath;

    public DictionaryModule(String databasePath) {
        this.databasePath = databasePath;
    }

    @Override
    public void configure(Binder binder) {
        Map<String, String> properties = new HashMap<>();
        properties.put("hibernate.connection.url", "jdbc:hsqldb:file:"+databasePath);
        binder.bind(EntityManagerFactory.class).toInstance(
            Persistence.createEntityManagerFactory("dictionary", properties));
    }
}

Dictionary Service and Data Provider

The dictionary service contains the "business logic" of the dictionary. It is responsible for creating dictionary entries. It can also create a data provider, which is needed by the view to display the stored dictionary entries.

@Singleton
public class DictionaryService implements Serializable {
    private static final long serialVersionUID = 1L;

    @Inject private DictionaryRepository repository;
    @Inject private DictionaryWordEntityFactory entityFactory;
    
    public void addWord(final String german, final String austrian) {
        assert german != null && !german.isEmpty();
        assert austrian != null && !austrian.isEmpty();
        
        DictionaryWordEntity entity = entityFactory.createEntity(german, austrian);
        assert entity != null : "Entity can never be null at this point";
        
        repository.add(entity);
    }

    public DictionaryDataProvider getDataProvider() {
        return new DictionaryDataProvider(repository);
    }
}

public class DictionaryDataProvider implements IDataProvider<DictionaryWordEntity> {
    private static final long serialVersionUID = 1L;

    private final DictionaryRepository repository;
    
    public DictionaryDataProvider(final DictionaryRepository repository) {
        this.repository = repository;
    }

    @Override
    public void detach() {
    }

    @Override
    public Iterator<? extends DictionaryWordEntity> iterator(
            final int first, final int count) {
        return repository.select(first, count).iterator();
    }

    @Override
    public IModel<DictionaryWordEntity> model(final DictionaryWordEntity entity) {
        return new Model<DictionaryWordEntity>(entity);
    }

    @Override
    public int size() {
        return repository.size();
    }
}

DictionaryRepository and DictionaryWordEntity

The repository is responsible for managing a list of entities. Entities are read only after they are created. Entities can only be created using a factory.

@Entity
public class DictionaryWordEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    private String german;
    private String austrian;
    
    public Long getId() {
        return id;
    }
    public String getGerman() {
        return german;
    }
    public String getAustrian() {
        return austrian;
    }
    
    public static class DictionaryWordEntityFactory {
        public DictionaryWordEntity createEntity(
                final String german, final String austrian) {
            DictionaryWordEntity entity = new DictionaryWordEntity();
            
            entity.german = german;
            entity.austrian = austrian;
            
            return entity;
        }
    }
}

@Singleton
public class DictionaryRepository implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @Inject private EntityManagerFactory emFactory;

    protected EntityManager newEm() {
        return emFactory.createEntityManager();
    }

    public void add(final DictionaryWordEntity entity) {
        assert entity != null;
        assert entity.getId() == null : "Add only works with new entities (where id==null)";
        
        EntityManager em = newEm();
        assert em != null : "Entity manager can never be null at this point";
        
        try {
            em.getTransaction().begin();
            em.persist(entity);
            em.getTransaction().commit();
            assert entity.getId() != null : "Entity always has an id at this point.";
        } finally {
            em.close();
        }
    }

    public int size() {
        EntityManager em = newEm();
        assert em != null : "Entity manager can never be null at this point";
        
        try {
            return ((Long) newEm().createQuery(
                "select count(dw.german) from DictionaryWordEntity dw")
                .getSingleResult()).intValue();
        } finally {
            em.close();
        }
    }

    public List<DictionaryWordEntity> select(final int first, final int count) {
        EntityManager em = newEm();
        assert em != null : "Entity manager can never be null at this point";
        
        try {
            return newEm().createQuery(
                "from DictionaryWordEntity dw order by dw.german asc")
                .setFirstResult(first).setMaxResults(count).getResultList();
        } finally {
            em.close();
        }
    }
}

The file "persistence.xml" which sets up an in-process HSQLDB is not shown here.

Coming up next

The next posting in this series will show how you can make all required assets available offline. This will enable us to see the page even if we have no connection to the server. To test it, we will simply shut the server down. So, stay tuned to see how we'll make this application available offline.

Do you have any comments regarding this blog posting? Pleas contact me! You can find the contact info at the bottom of this page.

Posting Type: 

SoCraTes 2012

This weekend I was at SoCraTes Conference in Rückersbach (near Frankfurt), Germany. Over 70 great software developers, in a hotel for the whole weekend, eager to learn and teach and share and have fun... This must work, right? And it did! The conference was absolutely, truly fantastic!

I did not agree with everything I saw there, but I don't want to spoil this post with too much criticism. I will (probably) write another post about things I see differently than some people at the conference.

Most of the attendees arrived on Thursday evening. There was fingerfood and beer, and we started to get to know each other. Then there was a World Cafe where we discussed what people were actually here for: The topic on every table was "What do you want to learn here". I am not sure if this was the right question to ask, because there was no real discussion. We only collected ideas. Anyway, it got us talking to each other, so it was ok. The rest of the evening was basically "having fun together".

The next two days were organized as "open space". This was my first open space, and I was really surprised how well it went. It was better than most of the "traditional" conferences I've attended so far. But, some other participants said that this was the best open space they have been to - so maybe not every open space conference is as great as this. I guess there is a more general rule at work here: Even a great technique can only work with the right people. And the right people were definitely there!

On the first day I gave a session where I showed some of my code. The intent was to show how I do testing, quality assurance, version control, etc. in a solo project. It quickly became a huge code review, which was very interesting. The audience criticised a lot of things, but the criticism was very constructive and the discussions were great. I really learned a lot in this session. And I got many useful tips what I could do better - I already started incorporating them into the application (zensmb).

In the evening Martin Heider hostet a "Marshmallow Challenge". Three teams had to build a free standing structure out of spaghetti, tape and string, with a marshmallow on top.

Our team won. I came - again - to the conclusion, that a little bit of thinking an prototyping before you start, and an incremental approach afterwards, are a great combination - for almost everything. We first tested how the spaghetti could support the marshmallow and how we could attach spaghetti to each other so that the result is as rigid as possible. After one third of the time, we still had nothing. Then we disassembled everything and built a small, very rigid tower with a marshmallow on top. Just about two thirds through we "delivered" our first increment (*). After that we built a larger structure, on top of which we attached the smaller tower. Our second increment was finished just about two minutes before the (18 minute) deadline. That's where we stopped.

The other teams tried to build a large structure first, only to discover that it could not carry the weight of the marshmallow. They spent the rest of the time trying to stabilize their structures. This worked amazingly well too: After 18 minutes, every team had a standing structure, and we won with only 1cm difference.

The second day was "open space" again. I proposed another session about "Testing with mockito and guice". Johannes Seitz had planned a talk about mocks in general, so we combined our sessions. We didn't really plan it (we did maybe 10 minutes of planning over lunch) but it went very well anyway.

The third day (Sunday) was a code retreat. It was very intense. The other two days were intense too, but this was different: I was really tired and exhausted afterwards. It was my first code retreat, and I probably want to do it again.

In closing I want to thank Andreas Leidig and Nicole Rauch for organizing this conference. And I wanted to thank everybody who was there - It was such a good experience, and you all contributed to that.

(*) "Two thirds through" sounds like we delivered pretty late. But "two thirds through" means 10-12 minutes in this case, and the other teams still had nothing at this point.

Posting Type: 

zenSMB: Landing page online

This blog posting is part of the Freelancer to entrepreneur series.

Finally! The landing page of zenSMB is online! Now I can tell you, dear reader, what I was working on in the last few weeks:

zenSMB makes managing your invoices and other documents easy! With zenSMB you can verify electronic signatures and VAT identification numbers and archive your invoices.

So, zenSMB is a service for freelancers and small and medium businesses. We work hard to make managing invoices a little easier. Right now, we are only at the beginning. When you go to zensmb.com, you can not do very much yet: You can request an invitation to our test phase, and you can subscribe to our newsletter. But: Under the hood, there are already some working features. We are confident that we can send out the first invitations to interested users within the next few weeks.

Posting Type: 

Horizontally aligned form fields with CSS (WAS: Vertically aligned...)

Update: When I wrote this blog post, I confused "vertically" with "horizontally". I hope you can forgive me. This is how you can really align divs vertically. This article shows you how to align form fields horizontally.

In the last few weeks I was doing a lot of HTML and CSS again (after working on a WPF right client application for 1 1/2 years). One of the first things I had to do was to design an input form where the form fields have labels and are horizontally aligned. Here is how I solved it.

First, change the box sizing to border-box. Because for me, CSS makes much more sense when box-sizing is border-box.

* { 
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
}

Alright, now let's align the form fiels. Here is the HTML form where the input fields should be horizontally aligned:

<form>
    <label>Radio Group:</label>
    <input type="radio"/> Option 1<br/>
    <input type="radio"/> Option 2<br/>
    
    <label>Some Input:</label>
    <input type="text"/><br/>

    <label>Another Input with a rather long caption:</label>
    <input type="text"/><br/>

    <input type="submit" value="submit"/>
</form>

The result looks quite ugly:

Option 1
Option 2


I started with this solution but I had to change it a bit. First, let's create the CSS rule for labels from the linked blog post:

label {
    float: left;
    width: 150px;
    text-align: right;
    margin-right: 0.5em;
}

The result looks like this:

Option 1
Option 2


Well, still a little crappy. The text of the third label is way too long. Also, we need a rule for form fields without a label.

label {
    float: left;
    width: 150px;
    text-align: right;
    margin-right: 0.5em;

    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    -o-text-overflow: ellipsis;
}
.form-field-no-caption {
    margin-left: 150px;
    padding-left: 0.5em;
}
<form>
    <label>Radio Group:</label>
    <input type="radio"/> Option 1<br/>
    <span  class="form-field-no-caption"><input type="radio"/> Option 2</span><br/>
    
    <label>Some Input:</label>
    <input type="text"/><br/>

    <label>Another Input with a rather long caption:</label>
    <input type="text"/><br/>

    <span  class="form-field-no-caption"><input type="submit" value="submit"/></span>
</form>
Option 1
Option 2


And, of course, this also work when you specify a relative width for the labels:

label {
    float: left;
    width: 20%;
    text-align: right;
    margin-right: 0.5em;

    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    -o-text-overflow: ellipsis;
}
.form-field-no-caption {
    margin-left: 20%;
    padding-left: 0.5em;
}
Option 1
Option 2


Before I discovered the blog post linked at the top of this article I used tables for horizontally aligning form fields. But a table is a rather ugly workaround in this case - and most of the time it is not necessary. Horizontally aligning form fields is not that hard in CSS - at least when you can use a fixed width or a width relative to the parent container for the labels.

You might also be interested in...

Posting Type: 

Pages

My name is David Tanzer and I have been working as an independent software consultant since 2006. I help my clients to develop software right and to develop the right software by providing training, coaching and consultanting for teams and individuals.

Learn more...

Subscribe to RSS - David's Blog