Category: Software

log files

Focus on your users and stakeholders. Dat is voor mij een belangrijke regel bij software ontwikkeling. En dat zijn niet alleen de eindgebruikers, maar ook degenen die onderhoud doen en de afdeling operations.

Voor de laatste groep is monitoring en configuratie erg belangrijk en voor beide de is goede logging van belang. Goede logging helpt bovendien het ontwikkelteam bij het oplossen van bugs en operationele problemen. Toch zie ik vaak dat logging, monitoring en configuratie een ondergeschoven kindje is.

Voorbeelden zijn log files waarin zoveel onzinnige informatie wordt geschreven dat de belangrijke en zinvolle meldingen ondergesneeuwd raken.

In principe maakt het niet veel uit welk logging framework gebruikt wordt als de basis maar gezond is. Naar mijn idee voldoet goede logging aan onder meer de volgende regels:

  • in productie en acceptatie test omgevingen worden alleen de info, warnings, error en fatal categorieen geschreven in de log files
  • fine en debug levels worden alleen geactiveerd als er een probleem is
  • de log levels moeten dynamisch aan te passen zijn per software component in geval van problemen
  • op info niveau worden meldingen gegeven van de hoofd processen die starten en eindigen, inclusief voldoende context informatie
  • een “chain of actions” kunnen identificeren door een uniek id te loggen
  • errors en fatal levels alleen gebruiken bij echte errors, en alle context informatie meegeven die voorhanden is; stacktrace, user id, waarde van input velden of method parameters, regel nummers en column van de input op moment van falen, relevante configuratie settings
  • bij opstarten van componenten alle configuratie settings loggen

Als ik een software project uitvoer let ik bij systeem test en vooral bij acceptatie test op de log files. Kan je goed volgen wat er in grote lijnen gebeurt met de info logging? Staan er geen errors of fatals in die genegeerd worden? Staan er op info logging geen zaken die beter op fine of debug niveau thuis horen? Als er een error in de log staat, is dan direkt duidelijk wat er aan de hand is?

Als dit niet het geval is neem ik meteen aktie om het te verbeteren.

Wat ik merk is dat developers en operations veel meer vertrouwen krijgen in de software en dat problemen snel kunnen worden opgelost.

Neem de volgende twee voorbeelden van een log file snippet:

INFO connecting to remote system
INFO getting sales.xml file from system
INFO processing file
FATAL cannot read file contents: conversion error
[stacktrace]
INFO processing file

en vergelijk met de volgende versie:

INFO processId: 837468874 connecting with FTP to account@archive.backoffice.org:2121
INFO processId: 837468874 getting file 'outgoing/sales.20100909.xml'
INFO processId: 647837443 start parsing file 'sales.20100908.xml'
FATAL processId 647837443 cannot convert 'null' to integer. File: 'sales.20100908.xml' line: 2244 column: 156
[stacktrace]
INFO processId: 837468874 start parsing file 'sales.20100908.xml'

In de eerste stukje log is het niet duidelijk dat er eigenlijk twee threads lopen. De FATAL hoort niet bij de twee INFO log statements daarboven, zoals in het tweede voorbeeld wel duidelijk is door toevoeging van het processId.

Het zal de lezer duidelijk zijn wat de benodigde effort is om de bug op te lossen in het eerste geval en het tweede geval. En de winst in effort is niet alleen in productie te incasseren, maar ook op alle test omgevingen.

Voor software ontwikkelaars is het een een kwestie van even stilstaan bij het schrijven van logger.log(…) statements. Het kan veel tijd en kosten besparen.

Automatische testen

Om de kwaliteit van software te garanderen is een goede test van groot belang.

Testen kan behoorlijk wat tijd kosten, vooral als er vaak een nieuwe release gemaakt wordt. Bij iteratieve ontwikkelmethoden is dat bijvoorbeeld iedere maand. Handmatig regressie testen doen is een kostbare zaak.

De oplossing lijkt voor de hand te liggen: automatiseer de testen.

Toch zie ik vaak dat het ook een hele investering is om automatische testen op te zetten en belangrijker nog, om de testen up-to-date te houden. Een unit test is snel geschreven, maar het bijhouden van een grote set van unit tests kost discipline van de software ontwikkelaars. Een eerste vereiste is dat de tests continue blijven werken. Een automatisch bouw proces met monitoring is essentieel. Alle tests groen is goed, een rode test mag niet voorkomen en moet direct worden verbeterd.

Unit tests werken dan ook het best als ze heel lokale tests uitvoeren. Bijvoorbeeld het testen van validatie regels of andere “stateless” functies.

De meer complexe unit tests die gebruik maken van mock objecten voor “dependend objects” zijn al snel bewerkelijk en lijken ook meer op integratie tests.

Waar ik meer waarde in zie is een automatische integratie test. Daaronder versta ik testen die software van voor naar achter testen in een integratie omgeving. Denk dan aan een automatische build waarbij het resultaat ook automatisch gedeployed wordt naar een volledige test omgeving, inclusief applicatie server, queueing software, database en ldap servers.

Door de opbouw van de test omgeving volledig te scripten zorg je ervoor dat je altijd een up-to-date omgeving hebt met de juiste configuratie en settings. Denk aan scripts die de applicatie server inrichten met onder andere datasources en system properties, database setup scripts en initiƫle data. Door van de juiste settings parameters te maken kunnen dezelfde scripts bovendien gebruikt worden om de hele otap straat in te richten.

Dit lijkt veel werk, maar als deze opzet vanaf het begin van het project wordt gekozen, kan er ook veel tijd bespaard worden. Bijvoorbeeld doordat er minder handmatige configuratie fouten gemaakt worden.

De testen zelf worden geschreven op de user interface. Bij web applicaties kan dit met behulp van tools als selenium of een watir script. Deze tools zijn behoorlijk op ontwikkelaars gericht. Als testers of analisten test cases schrijven kan er gekozen worden voor bijvoorbeeld fitnesse.

Met deze scripts kunnen complete use cases worden uitgewerkt, zoals het aanmelden van nieuwe gebruikers, iets kopen op de website of het invullen en doorlopen van wizards.

Bij deze testen worden alle onderdelen en lagen van de software applicatie geraakt en getest, alsof er een echte gebruiker aan de gang is.

Uitdagingen blijven er, zoals het constateren dat er iets mis is gegaan (plotseling een error pagina in plaats van de volgende wizard pagina). Als het te testen systeem (of SUT: System Under Test) ook met andere systemen communiceerd kan daar gewerkt worden met stubs. Deze kunnen slim zijn en reageren met verschillende responses op bepaalde requests.

Vaak gaat de communicatie met andere systemen tegenwoordig via SOAP over http. Met soapui is dan redelijk eenvoudig een slimme stub of proxy te bouwen. Mijn ervaring is dat soapui stubs, als het er wat meer worden, lastig te onderhouden zijn. Met SOAP stubs in mule esb, met bijvoorbeeld groovy scripts voor de slimme stub, heb ik betere ervaring.

Automatisch testen blijft een hele uitdaging en een behoorlijke investering. Met de juiste tools en een pragmatische aanpak kan je een eind komen.

Mix and Match CSV data with Ruby

robabank

funny

Multiple times a year I have to submit my VAT or ‘BTW’ tax forms. As I have a small business to run, I use good old Excel to calculate the numbers for the last Quarter. Most of the input comes from the payment transactions of my online bank account.

I found myself copy-pasting all relevant transactions from the financial transactions overview screen in the browser to the appropriate excel columns. Very boring and repetitive work. Time consuming as well. Translating dates into the appropriate format, mixing the columns to get the expected order, …

To go with my philosophy to automate everything that is repetitive, I created the following solution that I know will save me time all upcoming VAT rounds.

There is an option to download all transaction data from my account to a comma separated values file, or CSV file. You can download transactions with a start and end date, which makes it easy to get a file with records for the last Quarter only.

Next, with a little Ruby script, extract all relevant rows and columns and put them in the right order.

Then copy-paste the resulting file to the area in my BTW excel sheet and … done.

For this I used the fastercsv library, which made parsing the file quite simple.

require 'rubygems'
require 'faster_csv'
require 'parsedate'

# parse a csv file from Rabobank online banking download
#
# copy-paste or import the resulting output into excel
#
# note: to install the gem for faster_csv, use 'gem install fastercsv' e.g. without underscore!
#
# This is input for the BTW excel file!
whole_file = File.open(ARGV[0]) 

FasterCSV.parse(whole_file) { |row|
  # "tb" seems code between own accounts: skip these
  # filter out the Debit records "D" or Credit records "C"
  if row[3] == "D" and not row[8] == "tb"
	# put together the description fields
    description = "#{row[6]} #{row[10]} #{row[11]} #{row[12]}".strip.gsub(/s+/, ' ').downcase
	# put date in expected format for excel
    date = ParseDate.parsedate("#{row[2]}")
    my_date = "#{date[2]}-#{date[1]}-#{date[0]}"
	# change comma into a dot for price field
    price = row[4].gsub('.', ',')
    puts "#{my_date}|#{description}|#{price}"
  end
}

Note that this is used with the download of the Rabobank (or “Rob-a-bank”, as pitched by Boom Chicago)

Guidelines for Struts2 projects – part 1

Struts2 is a very flexible framework. And with so much flexibility, consistency may be at stake.

Lot’s of freedom brings rules to adhere to. As a team.

Here are some of the rules that I found after some team meetings on how to work with Struts2. As the application architect, some guidelines needed to be put on paper.

Rule: do not use interceptors for business related code and logic; interceptors are the Framework

It is very tempting to create lots of interceptors that will gather all kinds of almost always needed data. For instance to always pull the main object for you application from the database and insert it into the current Action. This results in Actions being dependent on a certain interceptor stack, otherwise it is missing business information. This information can be very specific to (one part of) the application. And if more interceptors are needed to process business information, your action cannot considered to be one coherent component.

So consider Interceptors being the Web Framework. In Struts 1 you would know the exact sequence of calls to the Forms and Actions ( validate, execute, …). In Struts2 this sequence can be tweaked by simply changing the interceptor stack. All developers should have a clear view on what the framework is, and not be dependent on specific interceptor stacks to have their Actions work correctly.

Not following this rule will make

  • Action code harder to read and understand, because there are hidden dependencies
  • testing more difficult, because you need the interceptor stack for testing your Action
  • struts2 configuration more complex, because you will probably end up with many different interceptor stacks
  • Think of it this way: interceptors should only deal with infrastructural concerns, such as logging, calling lifecycle methods on the Actions, transforming values from web pages to actions, security, and others. So if you write an interceptor, check if the interceptor can also be used in other applications, and have no dependencies on the business model of the application you are working on.

    Also watch for having too many parameters on interceptors. Sometimes business logic can creep into an interceptor this way. You will notice that for some actions you will need to set a param of the interceptor to false to exclude some logic from running in that particular interceptor stack. Maybe you are better of to include the logic explicitly in the Action, or in some component that the Action can delegate to. This will make the logic in your Action explicit and better to understand.

    More guidelines will follow…

    What is this [Z?

    Braille ZIn a dump from a Java Virtual Machine, just before an out of memory, there was a list of numbers of instances of objects in the JVM. So there was a table with a ‘number of instances’ column and a ‘type’ column.

    One of the types was [I, so probably arrays of integers, but what are the [Z's?

    Here is a little program to find out:

    public class DumpClassType {
    
        public static void main(String[] args) {
            System.out.println(“String[0][0][0]: ” + new String[0][0][0].toString());
            System.out.println(“short[0]       : ” + new short[0].toString());
            System.out.println(“double[0]      : ” + new double[0].toString());
            System.out.println(“long[0]        : ” + new long[0].toString());
            System.out.println(“char[0]        : ” + new char[0].toString());
            System.out.println(“int[0]         : ” + new int[0].toString());
            System.out.println(“boolean[0]     : ” + new boolean[0].toString());
            System.out.println(“byte[0]        : ” + new byte[0].toString());
            System.out.println(“float[0]       : ” + new float[0].toString());
        }
    
    }
    

    And the output:

    String[0][0][0]: [[[Ljava.lang.String;@3b2a3b2a
    short[0]       : [S@3c183c18
    double[0]      : [D@3c8c3c8c
    long[0]        : [J@3d003d00
    char[0]        : [C@3d743d74
    int[0]         : [I@3de83de8
    boolean[0]     : [Z@3e5c3e5c
    byte[0]        : [B@3f443f44
    float[0]       : [F@3faa3faa
    

    So, obviously, an array of booleans!

    unhandy windows key shortcuts with eclipse

    Finding the advanced keyboard settingsSome developers around me were complaining that every once in a while the keyboard settings would change while programming eclipse. I was not aware, but it turned out I changed my settings years ago to prevent this from happening. Here is the cause:

    The default company settings of Windows XP are to have the keyboard layout key shortcuts enabled by default.

    These are located in ‘Control Panel’ > ‘Regional and Language Options’ > ‘Languages’ > ‘Details’ > ‘Key Settings’ > ‘Change Key Sequence’

    ‘CTRL + shift’ or ‘left Alt + shift’ switches between keyboard layouts. For instance between US and Dutch/NL layout. For the Dutch layout, for instance, you need to type quotes (“) followed by a space to display them in the editor. Very unhandy for programmers.

    This combination is also used in Eclipse: for instance “shift + ctrl + T” to popup the Type selector.

    Disable the microsoft windows key binding by unchecking the designated checkboxes!

    How thread safe are Struts2 Interceptors?

    Struts2 In a project where we are using struts2 we wanted to create two custom interceptors. One to start a Transaction at the start of the interceptor stack and one to close that Transaction at the end of the interceptor stack. This would cause all (read only) database calls for the prepare method and all the conversion and validation to run in one transaction.

    Instead of heaving two, very similar, interceptors to start and stop transactions, it’s more convenient to have only one with a parameter to switch start and stop mode. But hold on… the Struts2 docs state clearly that interceptors must have no state. Is setting a parameter on an interceptor, that is stored in a member variable not just that: state?

    And if there is only one interceptor, that will surely fall over when used simultaneously in two or more request, each with different parameters to the interceptor. (Remember that you can override interceptor parameters per stack, or even per action, think of the “excludeMethods” parameter of the validate interceptor). This cannot be a design flaw, or what?

    Unable to find an answer quickly in my Struts2 book or with Google, I put Struts2 to the test. For the first time I tried running Eclipse europa (on the MacBook Pro) with the Web Tools Platform and I was pleasantly surprised at its first time ease of use. Just go to the Eclipse update feature and select WTP. And I had installed Tomcat 6 already, so pointing to the installation directory for the server configuration was enough to get started.

    Next, I downloaded the struts2-blank-2.0.11.1.war and imported this into the server configuration. Setting a breakpoint in the example code worked just fine. Just browsed to

    http://localhost:8080/struts2-blank-2.0.11/index.html

    and bingo: Eclipse stopped at the breakpoint.

    Now for the test. First I changed the example.xml to include a new Interceptor:

    
    		
    			
    			
    				
    green
    				
       				
      				
    red
      				
    			
    		
    
            
                /example/HelloWorld.jsp
                
    orange
                
            
    
           
                /example/HelloWorld.jsp
                
    blue
                
            
    

    Next, implementation of the interceptor:

    package example;
    
    import java.util.logging.Logger;
    
    import com.opensymphony.xwork2.ActionInvocation;
    import com.opensymphony.xwork2.interceptor.Interceptor;
    
    public class ThreadSafeInterceptor implements Interceptor {
    
    	private static final long serialVersionUID = 1964558869343105305L;
    
    	private Logger log = Logger.getLogger("threadsafe");
    	private String toggle;  
    
    	public void destroy() {
    		log.info("Object: " + this);
    	}
    
    	public void init() {
    		log.info("Object: " + this);
    	}
    
    	public String intercept(ActionInvocation ai) throws Exception {
    		log.entering(this.toString() , "intercept()");
    		log.info("Object: " + this + " toggle before: " + toggle);
    		String result = ai.invoke();
    		log.info("Object: " + this + " toggle after: " + toggle);
    		return result;
    	}
    
    	public String getToggle() {
    		return toggle;
    	}
    
    	public void setToggle(String toggle) {
    		log.info("Object: " + this + " setToggle("+toggle+")");
    		this.toggle = toggle;
    	}
    
    }
    

    If there was only one interceptor, and the parameters would be set to change color, it must show up that the interceptor gets confused while running!

    Here is what the console showed:

    WARNING: Settings: Could not parse struts.locale setting, substituting default VM locale
    Apr 22, 2008 10:04:40 PM example.ThreadSafeInterceptor setToggle
    INFO: Object: example.ThreadSafeInterceptor@89c698 setToggle(green)
    Apr 22, 2008 10:04:40 PM example.ThreadSafeInterceptor init
    INFO: Object: example.ThreadSafeInterceptor@89c698
    Apr 22, 2008 10:04:40 PM example.ThreadSafeInterceptor setToggle
    INFO: Object: example.ThreadSafeInterceptor@4d3241 setToggle(red)
    Apr 22, 2008 10:04:40 PM example.ThreadSafeInterceptor init
    INFO: Object: example.ThreadSafeInterceptor@4d3241
    Apr 22, 2008 10:04:40 PM example.ThreadSafeInterceptor setToggle
    INFO: Object: example.ThreadSafeInterceptor@e5a19e setToggle(orange)
    Apr 22, 2008 10:04:40 PM example.ThreadSafeInterceptor init
    INFO: Object: example.ThreadSafeInterceptor@e5a19e
    Apr 22, 2008 10:04:40 PM example.ThreadSafeInterceptor setToggle
    INFO: Object: example.ThreadSafeInterceptor@d5cdab setToggle(blue)
    Apr 22, 2008 10:04:40 PM example.ThreadSafeInterceptor init
    INFO: Object: example.ThreadSafeInterceptor@d5cdab
    Apr 22, 2008 10:04:40 PM com.opensymphony.xwork2.util.ObjectTypeDeterminerFactory 
    

    Ahh… at startup actually three instances are created! One for each interceptor-ref! So each one holds just one set of parameters, one state. Also note that first the toggle parameter is set and then the init is called. So you could potentially use the parameter value in the init method. Hmm, does it make sense to define the private toggle field as final as well, so it cannot change?

    Next in the log, when pointing the browser to

    http://localhost:8080/struts2-blank-2.0.11/example/HelloWorld.action

    :

    Apr 22, 2008 10:07:36 PM example.ThreadSafeInterceptor intercept
    INFO: Object: example.ThreadSafeInterceptor@e5a19e toggle before: orange
    Apr 22, 2008 10:07:36 PM com.opensymphony.xwork2.validator.ActionValidatorManagerFactory 
    INFO: Detected AnnotationActionValidatorManager, initializing it...
    Apr 22, 2008 10:07:36 PM example.ThreadSafeInterceptor intercept
    INFO: Object: example.ThreadSafeInterceptor@4d3241 toggle before: red
    Apr 22, 2008 10:07:37 PM example.ThreadSafeInterceptor intercept
    INFO: Object: example.ThreadSafeInterceptor@4d3241 toggle after: red
    Apr 22, 2008 10:07:37 PM example.ThreadSafeInterceptor intercept
    INFO: Object: example.ThreadSafeInterceptor@e5a19e toggle after: orange
    

    Orange, red, red, orange… looks good, the green interceptor is hidden from this stack! Note that only the first occurence of the two interceptors in the stack is overridden. Mind this when using the “param-prepare-param” stack with possible parameter overrides for the param interceptor!

    Calling this url:

    http://localhost:8080/struts2-blank-2.0.11/example/HelloWorldBlue.action

    shows:

    Apr 22, 2008 10:08:10 PM example.ThreadSafeInterceptor intercept
    INFO: Object: example.ThreadSafeInterceptor@d5cdab toggle before: blue
    Apr 22, 2008 10:08:10 PM example.ThreadSafeInterceptor intercept
    INFO: Object: example.ThreadSafeInterceptor@4d3241 toggle before: red
    Apr 22, 2008 10:08:10 PM example.ThreadSafeInterceptor intercept
    INFO: Object: example.ThreadSafeInterceptor@4d3241 toggle after: red
    Apr 22, 2008 10:08:10 PM example.ThreadSafeInterceptor intercept
    INFO: Object: example.ThreadSafeInterceptor@d5cdab toggle after: blue
    

    As expected it uses the blue instance of the ThreadSafeInterceptor.

    Adding two param fields to see if both interceptors can be “replaced” with new ones for one action fails. Using a second black param entry as follows:

           
                /example/HelloWorld.jsp
                
    blue
    black
                
            
    

    shows:

    Apr 22, 2008 10:51:06 PM example.ThreadSafeInterceptor intercept
    INFO: Object: example.ThreadSafeInterceptor@9d963f toggle before: black
    Apr 22, 2008 10:51:06 PM com.opensymphony.xwork2.validator.ActionValidatorManagerFactory 
    INFO: Detected AnnotationActionValidatorManager, initializing it...
    Apr 22, 2008 10:51:06 PM example.ThreadSafeInterceptor intercept
    INFO: Object: example.ThreadSafeInterceptor@1b5077 toggle before: red
    Apr 22, 2008 10:51:07 PM example.ThreadSafeInterceptor intercept
    INFO: Object: example.ThreadSafeInterceptor@1b5077 toggle after: red
    Apr 22, 2008 10:51:07 PM example.ThreadSafeInterceptor intercept
    INFO: Object: example.ThreadSafeInterceptor@9d963f toggle after: black
    

    The blue one does not even get created! Bummer… Well, we might be stretching the framework a bit, but it’s good to know the interceptors are really thread safe. And it brings the question: is it smart to use two of the same interceptors in one stack?

    Time for one more try, lets make a second interceptor entry for the same class with a different name: threadsafe2.

    		
    			
    			
    			
    				
    green
    				
       				
      				
    red
      				
    			
    		
    

    Now we can use:

           
                /example/HelloWorld.jsp
                
    blue
    black
                
            
    

    And this just works! The console writes out: blue, black, black, blue. Great!

    Pijpen op Marktplaats

    Niet zo lang geleden zag ik een presentatie van iemand van Yahoo, over de architectuur van Yahoo Pipes. Ik kende die site nog niet, maar na de presentatie wilde ik er snel iets mee doen. Wat een geweldige user interface, in een browser met Javascript. Je sleept en plaatst wat kleine componenten bij elkaar die vervolgens door pijpen aan elkaar geknoopt worden. De gegevens stromen via die pijpen van componentje naar componentje. Een kind kan de was doen. Zo heb je componenten om web pagina’s of RSS feeds op te halen en componentjes die kunnen filteren of die tekst kunnen vervangen met behulp van reguliere expressies. En je kan makkelijk geografische informatie toevoegen en de resultaten op een kaart weergeven.

    Mijn idee was om op een zinnige manier de zoekresultaten van Marktplaats onderhanden te nemen. Dat was makkelijker dan ik dacht, want de resultaten zijn met een rss feed op te halen. Je ziet het rss symbooltje rechts onder in de zoek resultaten pagina. Na een uurtje proberen had ik een simpele pipe klaar. Zie het resultaat hieronder of speel met de pipe zelf. Je kan de pipe ook eenvoudig toevoegen aan je iGoogle pagina, dan kan je nieuwe zoekresultaten meteen spotten.

    Terug op marktplaats zag ik dat ze deze kaart functionaliteit al zelf bieden, dus veel voegt het niet toe… maar grappig is het wel en er is vast nog iets heel intelligents mee te bouwen.

    P.S. je kan zo’n pipe clonen en een eigen versie maken…

    Het lijkt erop dat de kaart in Firefox grijs blijft?! Is dit een bug?

    Java and the Client, will they ever be happy?

    In a recent interview (well, that’s a big word, I was asked a question about Java…) I mentioned that there is still a future for Java on the Client. But is there?

    There are so many Rich Internet Application frameworks and solutions nowadays: Flex, Lazlo, Ajax-based, Silverlight, etc… it dazzles the mind. What to pick? Why learn yet another tool, configuration and/or language?

    As a Java developer, would it not be wonderful to do everything in Java. Drop the XML configuration and remoting. No JSPs or inside-out HTML, tags with HTML, Javascript, XHTML, CSS, and what not…

    And I was amazed with the requirements of web applications: master-detail updates, dynamic drop downs, complex grids with excel behaviour, drag and drop, tabs… and all the roundtrips that are needed to get things done. We’ve come a long way now (see Google apps), but I think there is still something to be said for plain old Java GUI’s.

    That was the entry point for a little project that I wrote. I’ve tagged it the “290% Java project” (stole that from the Oracle statement of a couple of years ago: “300% Java…”): Java in the Data layer, the Application Server layer and the Client layer.

    How to accomplish this? Create a Swing or RCP “lite weight” client. Make this available via Java Web Start. Communicate simple POJOs via the Burlap and/or Hessian protocol, using a Servlet in Tomcat Server. Also use those protocols to store the data on disk (no database!). The remote server calls are defined by a clean Java interface. To create a Swing UI use a tool such as JFormDesigner (I hear good things of Matisse nowadays).

    The client app runs both on Windows and the Mac, using Java 5 or 6.

    This app is now being used for almost 2 years in a row, daily, both at home and at the workplace by around 10 people. It is like a bulletin board for a group, that is closed. Everybody in the group can read all messages, but you can specify who the message is intended for. Furthermore, it shows you directly if the message has been read by all that it is intended for (a feature that I did not come acros in email or other bulletin boards), and when it was read.

    It was such a pleasure to create a UI in Java after all the struggles in the Browser world.

    I presented this approach to a group of Java developers. I was surprised to learn that there is fear to start using this “all Java architecture” approach. The reasons being that there skills to program Swing or RCP are hard to find, JSP and JSF programmers are much easier to find. (Is this true?) Also, Swing seems to be considered hard to do and still has a reputation of being slow. Well… look at NetBeans; great job for a Swing ui! I just love the MVC model of it.

    Some other points:

  • Rolling out a new version of the app is as simple as installing a new server side app: just put the news jars on a central site and Java Web Start will automatically get that new version when users open the application.
  • You can make a shortcut from the desktop to a JWS program.
  • You can use the same POJO model for your data on the server and on the client.
  • The model can be effectively cached in the client app. You can make it smart. In my proof of concept there is a version check of the model on the server. If it has not been changed, there is no need to refetch it. If it has changes (e.g. new or updated messages), only send the delta.
  • You can make use of third party jars on the client machine. For instance to generate PDF docs on the fly using iText. This relieves the server of a lot of memory and processor consumption.
  • You can make truely amazing interfaces using Java 2D, Java 3D, jGoodies, and maybe even JavaFX (there is a JavaFX demo on the Hessian RIA page, I discovered recently!)
  • You can use peer-to-peer architectures to communicate via clients directly (a la Azureus :-) )
  • There are some things to be aware of, or “some interesting issues”:

  • How to deal with security and authorisation.
  • How to secure your data over the line. Note that I used https for all communication. It even works behind corporate firewalls!
  • You need to sign all jars to make communication available, or file system access. Not a big deal with a few lines of ant script.
  • You also need to sign with a trusted certificate, so you will not get annoying popups during the app startup. These are quite expensive for a proof of concept (couple of hunderds of Euros). Although you can use a free Thawte personal certificate that is intended for e-mail.
  • There are some caching issues with JWS. Workaround that by putting version numbers in the jnlp filename or url.
  • What about using a very lite weight jump start client, based on Rich Client Platform of eclipse? You would only need Java Web Start for that little client. And then you can use the OSGi plugin model to dynamically load your app (or portal like app!) with the already present upgrade feature in eclipse.
  • All in all, I really like this concept. Who dares to make the jump??? I think it is really suited for intranet apps that have complex GUI requirements, and need to be very responsive…

    Let me know!

    Simple Screenshot tool for Windows

    Here is a handy little tool that I use to make screenshots on windows: ScreenHunter.
    It can make screenshots of a selected window, but also of any part of the screen. Including mousepointer and drop down menus if needed! And the most simple edition is free, but effective.