Monday, November 21, 2011

Notes From The BarCamp #BCMZ 2011 In Mainz Day One

Mein erstes BarCamp und eine Tolle Erfahrung.
Aufmerksam wurde ich vor einigen Jahren als ich noch in Eschborn wohnte und dort das erste Frankfurter BarCamp statt fand. Leider hatte ich den Event einen Monat zu spät entdeckt. Seit dem hat es 3 Jahre gedauert bis ich es endlich geschafft habe.
Als ich nach dem zweiten Tag erschöpft nach Hause kam, habe ich mir meine Notizen angeschaut und mich gefragt was ich damit mache. Es währe zu schade wenn nur ich einen Nutzen davon hätte. Deshalb möchte ich jedem Interessiertem einen Einblick geben.

Best Practice: Social Media und Customer-Experience-Mgm in einem Start-up Unternehmen

Vortrag war von Philipp Hormel, einem Mitarbeiter von Flinc.
Flinc ist ein junges Startup-Unternehmen das an der Hochschule Dieburg entstand.
Es handelt sich um einen online Mitfahr-Dienst der nicht nur für lange Strecken und einen gewissen Planungshorizont Dienste anbietet wie mitfahrzentrale.de sonder auch kurze Strecken und spontane Mitfahrten ermöglicht.
Der Vortrag ging um ‘Social Media Guidelines’ und war sehr kurzweilig mit vielen realen Beispielen aus ihren Kundenbeziehungen. Diese wurden aber erstaunlicherweise, egal wie heftig sie ausfielen, positiv oder mindestens neutral dargestellt.
Sie zeigten wie man “NEGATIV SCHREIENDE COMMENTARE” in positive wandeln kann. Was mich etwas überraschte war, dass das Unternehmen seit einiger Zeit seine Dienste anbietet aber fast ausschließlich Bussiness-Angel finanziert ist und noch keinen Plan hat mit welchem Bussiness-Modell sie Geld verdienen wollen. Es hieß, um das Thema kümmern wir uns im nächsten halben Jahr ;)
Leider habe ich im Anschluss niemaden von Flinc getroffen um die Frage zu stellen, wie sinnreich es war auf die dynamische Sprachplattform Ruby zu setzen und ob sie es im nachhinnein bereut haben in Punkto Performance.
Eine schön Idee fand ich es noch, dass sie für größere Firmen vorhabe einen Dienst anzubieten, der es den Mitarbeitern ermöglicht Firmen-Intern eine Mitfahrgelegenheit zu finden.
Um ins Fernsehen zu kommen gab es auch einen Tip. Am besten startet man eine PR-Release-Tour zu einem geplanten Zeitpunkt. Macht sich bei den Printmedien bekannt und versucht dort Artikel zu bekommen. Danach schreibt eh jeder von jedem ab und die Sender werden auf einen aufmerksam.

FrontEnd Best Practices

Vortrag von Jens Grochtdreis.
Ich bin Webtechnologie-Newbie und der Inhalt hat mich überfordert. Was ich aber mitgenommen habe ist, das die Trennung zwischen Semantik und Design wichtig ist. Zu oft vergessen Inhalteanbieter das eine Überschrift mit ausgezeichnet werden sollte, statt mit einem <div> oder <span>-Tag der toll designed ist. Suchmaschinen oder prinzipiell Maschinen können sollche Tags nicht auswerten.
Als Beispiel wurden die FAZ oder die Süddeutsche genannt. Einfach mal nachschauen, es gibt zig Überschrifen auf der Seite aber nur einen einzigen Überschriftentag!
Manche Slides kamen von failblog.org.
Eric Meyer wurde als CSS-Halbgott bezeichnet.
Lustig fand ich seine Bezeichnungen für Browser und Plattformen:
iPhone - iFöne
IE - InternetExplodierer
FireFox - FrickleFox
Er selbst ist YAML-Ferfechter. Überhaupt war er Ferfechter von Frameworks im Web-Umfreld und sagte dont’t repeat yourself, ich glaube aber eher das eher das er don’t repeat itself meinte.

Nach uns die Zukunft

Von Markus Seim ins Leben gerufenes und ambitioniertes Blog-Projekt.
Es versucht das Thema Nachhaltigkeit im deutschsprachigen Raum zu bereichern.
Diese Session erregte mein bessonderes Interesse, da ich seit einiger Zeit versuche selbst aus dem Kreislauf des blinden Konsums zu kommen und nachhaltiger zu handeln.
Besonderes Interesse habe ich für das Thema Nahrung das Markus auch als zentrales Thema für den Blog sieht. Nirgendwo anders kann man als Konsument so viel bewirken. Vielleicht werde ich mich dort mit einigen Artikeln über meine eigenen Erfahrungen beteilligen.
Die Session selbst war von Seiten der Zuhörer sehr kritikreich. Es wurde von allen Teilnehmern mit bohrenden Fragen versucht herauszufinden, warum es ein weiteres Blog mit diesem Thema geben sollte. Welches spezielle Ziel verfolgt wird, welche Strategie, das journalistischer Anspruch eher negativ besetzt währe und so weiter …
Hinter den Kulissen gibt es bisher 7 Mitglieder von den 3 zum Kernteam gehören. Markus hatte im ersten Schritt versucht einen eigenen Blog zu starten ist aber nach dem allseits bekannten Motivationsschub versandet. Danach traf er in Berlin eine Gleichgesinnten und startete mit ihm diesen Gemeinschaftsblog.

Backend Entwicklung für Mobile Apps

Von Marcel Etzel der bei match2blue arbeitet.
Es ging um eine firmeninterne Eigenentwicklung ener Backendlösung. Sowas gibts schon aber ihr Fokus liegt auf der Echtzeitverteilung von Lokationsbezogenen Daten. Als DB wird NoSQL verwendet, das gegenüber relationalen Datenbanken erst den Dienst für hohe Abfragehäufigkeiten ermöglicht. Dieses spezielle Thema ist nichts für mich.

Leiterplattenlayout erstellen

Von Oliver Perialis.
Er hat sich 2006 während seiner Abschlussarbeit mit Mikrocontrollern beschäftigt und sich im Anschluss mit einem eigen entwickelten Produkt selbständig gemacht.
Es handelt sich um eine Bluetooth-Bridge für Nikon Fotokameras zu einer beliebigen Bluetooth-GPS-Maus. Er vertreibt sie über seine Webseite foolography.
Da ihm der Einstieg in die Entwicklung von Leiterplatinenprototypen schwer gefallen ist, hat er diese Session zur Erstellung von Leiterplattenlayouts und morgen für das Ätzen von Platinen angeboten um anderen den Einstieg zu erleichtern.
Er hat die Sessions auch dazu genutzt publik zu machen das er vorhat ein FabLab in Wiesbaden ins Leben zu rufen. Soweit ich verstanden habe, hat er schon einen Lasercutter und möchte die Werkzeugkiste um einen 3D-Drucker erweiern.
Für das Layouten verwendet er die Freeware Version von EagleCad von CadSoft. Sie hat die Einschränkung Leiterplatten nur in der Größe 100 x 80 mm und mit maximal 2 Layern designen zu können. Er empfiehlt obwohl es schon viele Bibliotheken mit allen Bauteilen gibt, sich seine eigenen zu erstellen da die meisten aus Amerika kommen und die Maße in Zoll sind.

Wie funktioniert Geld?

Von Alexander Boerger.
Er ist eigentlich Künsler und kein Wirtschaftler. Nachdem er einem Artikel in der brand eins über ein alternatives Währungssystem gelesen hatte, packte ihn das Thema so sehr, das er sich seit dem alles aus diesem Bereich angelesen hatte.
Die Session war in zwei Teile aufgeteilt. Der erste ging um die Funktionsweise des Geldes bzw. des aktuellen zinsbehafteten Finanzsystems wie wir es haben. Die Aussage war, Zinsen sind die Wurzel allen Übels und bringen jedes Finanzsystem früher oder später zu Fall.
Im zweiten Teil ging es um eine Alternative, angelehnt an dem Inhalt des brand eins Artikels, einer Chiemgauer Gemeinde mit ihrer Regionalwährung dem Chiemgauer und dem von einigen Wissenschaftlern entworfenen Modell eines negativen Zinses. Nach bestimmten Zeitintervallen 1 Jahr, ½ oder viertelhährlich verliert man einen festgelegten Prozentsatz seines Geldes. Das würde die Geldmenge nicht ins Unendlich wachsen lassen, das Horten von großen Mengen unwirtschaftlich machen und die Umlaufgeschwindigkeit steigern. Alles in allem eine sehr interessante Session mit einigen neuen Anregungen die Welt nicht immer so zu akzeptieren wie sie ist.
Zum Schluss lancierte er dieser Session sein Projekt futuremoney.org. Zur Zeit noch ein Blog aber mit große Ambitionen für die Zukunft. Er plant für nächstes Jahr ein BarCamp zum Thema Geld. Ich werde dabei sein.

Friday, October 21, 2011

Generating Traffic For Your Blog

I have my blog for nearly two month now. My statistics show me 500 page views whats nice. But when I look at the traffic sources 80% comes from stackoverflow where I postet some links to some of my postings. There is nearly no traffic from search engine requests.

The first good idea to find out what is happening is to look at this site:
http://www.wholinks2me.com


This means only 26 of my 35 posts are actually indexed from google and the other two big search engines are unaware of me.

Search engine submission
Submit your blog to all search engines manually:
https://ssl.bing.com/webmaster/SubmitSitePage.aspx
https://siteexplorer.search.yahoo.com
http://freewebsubmission.com/

Sitemaps
Sitemaps are xml documents that allows you to inform search engines which pages should get crawled. If you are using blogspot, the automatic generated sitemap contains only 26 of your most resent pages.
To get all of your postings indexed, submit your own sitemap.
This can be done in the google webmaster-tools.

Add your blog.
Go to 'website-configuration' and there to 'xml-sitemaps'.
Hit the button 'submit xml-sitemap'
In the dialog insert the following link to your atom feed.

 atom.xml?redirect=false&start-index=1&max-results=500  

If you got more than 500 pages add them subsequently in 500 blocks.

 atom.xml?redirect=false&start-index=501&max-results=1000  
 atom.xml?redirect=false&start-index=1001&max-results=1500  

Tuesday, October 18, 2011

Android: How The Preference Framework Works

Every good application needs preferences that enables the user to customize and personalize it to his needs.
Android provides us with a preference framework ready to use. Like the rest of the UI, you have the choice to define your preference declaratively or programmatically. Android stores the preferences as key-value pairs of primitive data types in a shared preferences object.



Example
We will develope a preference activity consisting of two categories, each with a preference in it. The first categorie is defined declarativeley in an XML document while the second is defined programmatically.

The declarative Way of creating preferences
Start a new Android project like I described it in Part0-2 of my 'Getting Started With Android' tutorial.

At first we have to layout the preference screen through an xml file. This file comes by convention in the folder res/xml that we have to create.

Create a new 'Android XML File' from the context menue of the folder. When the dialog pops up, select the resource type 'Preferences', name it my_preferences.xml and choose PreferenceScreen as the root element.

 <?xml version="1.0" encoding="utf-8"?>  
 <PreferenceScreen  
      xmlns:android="http://schemas.android.com/apk/res/android">  
 </PreferenceScreen>  

The preferences UI consists of three basic class types.
PreferenceScreen
It is a container type. It can hold childs of the type PreferenceCategory, Preference and even PreferenceScreen itself to build up a nested preference UI.
PreferenceCategory
Groups Preferences together under one common title.
Preference
The actual preference that is a build in (EditTextPreference, CheckBoxPreference, ...) or custom preference.

 <?xml version="1.0" encoding="utf-8"?>  
 <PreferenceScreen  
      xmlns:android="http://schemas.android.com/apk/res/android"  
      android:key="prefscreen">  
   <PreferenceCategory   
        android:title="category 1"   
        android:key="@string/pref_cat_1_key">  
     <EditTextPreference   
          android:key="@string/pref_edittextpreference_key"   
          android:title="EditTextPreference"   
          android:dialogTitle="EditTextPreference"   
          android:dialogMessage="message"   
          android:summary="summary"/>  
   </PreferenceCategory>  
 </PreferenceScreen>  

For the sake of getting things done quick in tutorials I used plain strings and not the string.xml from the resources.
To bring the my_preferences.xml to the screen, extend an Activity from the class PreferenceActivity. In the onCreate() methode, inflate the ui from the file.

 public class MyPreferenceActivity extends PreferenceActivity {  
      @Override  
      protected void onCreate(Bundle savedInstanceState) {  
           super.onCreate(savedInstanceState);  
           addPreferencesFromResource(R.xml.my_preferences);  
           EditTextPreference etpUsername = (EditTextPreference)
                      findPreference(getString(R.string.pref_edittextpreference_key));  
      }  
 }  

Dont't forget to add the Activity via the AndroidManifest.xml otherwise the app will crash when it can't find it.

 <activity   
      android:name=".MyPreferenceActivity"   
      android:label="Preferences">  
 </activity>  

Now we have to implement the code that opens the MyPreferenceActivity from the options menue.
Create a new menu file 'my_menu.xml' in the folder res/menu.

 <?xml version="1.0" encoding="utf-8"?>  
 <menu  
  xmlns:android="http://schemas.android.com/apk/res/android">  
   <item android:title="@string/menu_item_preferences"   
        android:id="@+id/menu_preferences"   
        android:icon="@android:drawable/ic_menu_preferences"/>  
 </menu>  

In the main Activity we override the following callback methods.

 @Override  
   public boolean onCreateOptionsMenu(Menu menu) {  
        super.onCreateOptionsMenu(menu);  
        MenuInflater mi = getMenuInflater();  
        mi.inflate(R.menu.my_menu, menu);  
        return true;  
   }  
   @Override  
   public boolean onMenuItemSelected(int featureId, MenuItem item) {  
        switch(item.getItemId()) {  
             case R.id.menu_preferences:  
                  Intent i = new Intent(this, MyPreferenceActivity.class);  
                  startActivity(i);  
                  return true;  
        }  
        return super.onMenuItemSelected(featureId, item);  
   }  

Start your Application and have a look at our new preferences screen with the first category.

The programmatic Way of creating preferences
Go back to the MyPreferenceActivity and add the following lines at the end of the onCreate() methode.
They will add a second PreferencCategory called 'category 2' with a CheckBoxPreference in it.

     protected void onCreate(Bundle savedInstanceState) {  
         ...  
         PreferenceManager preferenceManager = getPreferenceManager();  
         PreferenceScreen preferenceScreen = 
                  (PreferenceScreen) preferenceManager.findPreference("prefscreen");  
         PreferenceCategory prefCat2 = new PreferenceCategory(this);  
         prefCat2.setTitle("category 2");  
         preferenceScreen.addPreference(prefCat2);  
         CheckBoxPreference checkBoxPreference = new CheckBoxPreference(this);  
         checkBoxPreference.setTitle("CheckBoxPreference");  
         checkBoxPreference.setSummary("summary");  
         prefCat2.addPreference(checkBoxPreference);  
     }  

With the help of the PreferenceManager we can obtain any preference that is declared so far. We fetch the PreferenceScreen because we want to add another PreferenceCategory at the end. To create an object of one of the preference types, just call the constructor of its class.
Plugging together the hierachy is done with the addPreference() method.

Start your Application and have a look at our new preferences screen.

Sunday, October 16, 2011

Cooking: Cauliflower Mushroom (Krause Glucke)

Today I found my biggest cauliflower mushroom ever.


They grow as parasites on the roots of trees. I found them only on conifers expecially on pine trees and there, very close to the trunk.



I had to make a trophy photo to demonstrate the huge size.


At home you have to cut it into smaler pieces to clean it.


This species is more dirty and harder to clean than any other mushroom.


I prefer to browse it with a shower head. The water should be cold.


But you can't get it super clean. There will be always some soil.
Pick out the big stuff like pine needles, pieces of bark or moth cocoons.


Cut the mushroom into finger thick slices and build up a schnitzel factory. Bath the slices in the scrambled egg and coat it with bread crumps.


Heat oil in a pan and fry the mushroom schnitzel until the pieces are dark brown.


Synonyms: sparassis crispa, cauliflower mushroom, krause glucke, fette henne

Friday, October 14, 2011

Using OSM data in GeoServer: Part3 - GeoServer

To install GeoServer you have a lot of options:


I choose the WAR because I want to extend the funktionality of the server later on (Not in this tutorial).
Go to the download page and get the stable WAR and unzip it.

geoserver-2.1.2-war.zip
geoserver.war

A WAR gets deployed into an application server. Also here you got a lot of options:


I choose GlassFish. Go to the download page and get the newest open source version.

glassfish-3.1-windows.exe

Installation folder C:\glassfish3
Http port 8080
Admin port 4848
start command C:\glassfish3\glassfish\bin\asadmin.bat start-domain domain1

Go to the admin site and deploy the GeoServer WAR.
Open your browser and type in 'http://localhost:4848'.
Click on Applications and there on the 'Deploy...' button.
Choose the geoserver.war ond click 'OK'.

Open the page 'http://localhost:8080/geoserver'.


The default username and password ist admin and geoserver.

If you want to know how GeoServer generally works I recomend to read the excellent GeoServer User Manual.

Now we want to bring the spacial data into the browser.

Go to 'Workspaces'.
Click 'Add new workspace'

Name osm
Namespace URI http://osm.de


Go to 'Stores'.
Click 'Add new Store'.
Click 'PostGIS - PostGIS Database'.

Workspace osm
Data Source Name osm data
host localhost
port 5432
database osm
user osmuser
passwd ***

Now you have to create for every table a layer.

Go to 'Layers'.
Click 'Add a new resource'
Choose 'osm:osm data'.

In the Tab 'Data' go to the point 'Bounding Boxes' and compute the native- and Lat/Lon bounding boxes from data.
Check in the Tab 'Publising' if the 'Standard Stil' is point for planet_osm_point and line for planet_osm_line, planet_osm_roads and polygon fro planet_osm_polygon.
Click 'Save'.

Go to 'Layer Groups'.
Click 'Add new layer group'
Name: osm group
Click 'Add Layer...' and add all 4 layers.
Click 'Geerate Bounds'.
Click 'Save'

Go to 'Layer Preview'.
Find the osm group and click 'OpenLayers'.





Using OSM data in GeoServer: Part2 - osm2pgsql

For this part we have to:

  1. get the OpenStreetMap(OSM) data that are called planet files.
  2. download the import tool osm2pgsql.
  3. import the osm data.

The OSM wiki has a page that describes how to download their data.
We choose geofrabrik. In the folder osm/europe/germany wie click on hessen.osm.bz2. This file is not to big (121 MB) for this example. Germany would take the whole day to import.


For osm2pgsql exist a compiled version to download from here.

 osm2pgsql -U postgres -W -c -s -d osm -S default.style hessen.osm.bz2  

The parameters:
-U Postgresql user name.
-W Force password prompt.
-c Remove existing data from the database.
-s Store temporary data in the database. This greatly reduce the RAM usage but is much slower. You are running this on 32bit system, so at most 3GB of RAM will be used. If you encounter unexpected exceptions during import, you should try this switch.
-d The name of the PostgreSQL database to connect.
-S Location of the style file. Defaults to /usr/share/osm2pgsql/default.style.

The import takes around 30 minutes.

Have a look in the osm-db. 5 new tables are there.
planet_osm_line
planet_osm_point
planet_osm_polygon
planet_osm_roads

In the next part I will show you how to visualize the date with GeoServer.

Using OSM data in GeoServer: Part1 - PostGIS

The first thing we need is a data base to store spacial data. Today ever big vendor has a spacial extension that is compliant with the simple feature specification from the OGC.
We could choose from the following data bases:
Oracle, MSSQL, MySQL, Postgres, ...

I pick Postgres with its spacial extention called PostGIS. It is an stable open source data base with no costs. This is the only data base I know for which a tool exists to import the osm data. It is called osm2pgsql.
  1. Download and install PostgreSQL.
  2. Update or manually install PostGIS
  3. Create a spacial db called osm with a new user
  4. Configure the connections

1. Download and install PostgreSQL

Get the one click installer from the download page. I choose the version 8.4 instead of the 9.x branch because it gots all the funktionality we need and it is the most stable and secure version.

One click it : postgresql-8.4.9-1-windows.exe

The installer will promt you:
Installation Directory: C:\Program Files\PostgreSQL\8.4
Data Directory: C:\Program Files\PostgreSQL\8.4\data
Password: <your password>
Port: 5432

At the end you get asked if you want to launch the Stack Builder.
Choose from the category tree 'spacial extensions' and then 'PostGIS 1.5'.

Alternatively you can install the extention by hand. There are instructions at the PostGIS site.

Open up the 'pg Admin III' tool from your start menue. In the objectbrowser you will find a data base called 'template_postgis' with two tables 'geometry_columns' and 'spacial_ref_sys'. If you need a new gis data base you use this as a template. So don't alter it.


Create a new Login-Roll called osmuser by right clicking on 'Login-Rollen'.
Create a new gis data base by right clicking on 'Databases' in the Tree.
Name: osm
Owner: osmuser
Template: template_postgis
Finaly you have to assing the osmuser to the tables in the data base.

This can also be done by the postgres console.

Now you are ready with the basic data base setup. Next part will show you how to import the osm data.

Using OSM data in GeoServer: Part0 - Introduction

In this tutorial I want to show you how we can setup our own OpenStreetMap(OSM) tiling server.
Most of the tutorials are writen for linux, I will use windows instead.

Several steps have to be done:
  1. Setting up a data base (PostGIS)
  2. Download and Import the OSM data via an importing tool (osm2pgsql)
  3. Setting up a GIS-Server (GeoServer) and generating the map tiles

Wednesday, October 12, 2011

Really Useful Java Data Visualisation And Charting Links

This is a collection of my favorite Java libraries for data visualisation, diagrams, charting and graphs.

JFreeChart
I use it for a small project (hotstock) of mine where I want to keep track of the stock market development.



prefuse















If you are an RCP developer or using SWT standalone, you might use the following libraries. They will blend into your projects.

Zest















Used to draw graphs and got the most important layouts.

GEF (MVC)

















You can draw interactive diagrams an expand them to an full working editor that uses the MVC pattern.

Thursday, October 6, 2011

Steve Jobs Died Today


Made from MacBook parts.



I was never a big fan of the restrictiv Apple strategy and the Steve Jobs hype until I saw this video on TED earlier this year. Great story.

Cooking: Parasol Mushroom (Schirmpilz)

The parasol mushroom is very common, easy to spot, hard to misstake and got a unique yummy taste.
It is best to treat it like a schnitzel. Clean it, bath it into a bowl of scrambled eggs and coat it with bread crumbs.
Put enough oil in a pan and fry it till deep brown.




Synonyms: Macrolepiota procera, parasol mushroom, schirmpilz

Tuesday, October 4, 2011

Eclipse: Exclude Packages From Autocomplete and Organize Imports

If you are programming SWT there are allways this nasty AWT classes that come up when using the autocomplete (Ctrl + Space) or organize imports (Ctrl + Shift + O). Sometimes they are even in the first line of the result.




If you want to get ride of it or other undesireable packages use Type Filters.

Preferences -> Java -> Appearance -> Type Filters
Add... the  java.awt.*  package.

Music From A Single Line Of C-Code



Now there are coders experimenting with “oneliners,” programs of around 16 bytes in size that accomplish what they need to in a single line of C. By running the code through an 8 kHz audio channel, demoscener viznut and others discovered these raw loops of entrancingly garbled computer music.

Cooking: Common Puffball (Flaschenstäubling)

This mushroom is one I collected never before. The wood is full of this little ones and it would be a shame to leaf them out. It is a good edible mushroom when young and insde homogeneous and white.
You can fry it with onions or use it in a soup.





Synonyms: Lycoperdon perlatum, common puffball, warted puffball, gem-studded puffball or devil's snuff-box

Friday, September 30, 2011

Quick SQL Reference

I know SQL very well, the idea of the basic operations and how I can combine them to achive on a higher level nearly everyting I need. But I have a bad memory about the exact syntax. Allways asking me how do I have to write the UPDATE statement?
This Post is a quick lookup of the SQL-Syntax.


key example
CREATE TABLE CREATE TABLE table_name ('column1' NVARCHAR(32),

'column2' NUMBER, ... )

DROP TABLE DROP TABLE table_name
TRUNCATE TABLE TRUNCATE TABLE table_name
INSERT INTO INSERT INTO table_name(column1, column2, ...)

VALUES(value1, value2, ...)
WHERE ...

UPDATE UPDATE table_name
SET column1=value1
WHERE
...

DELETE FROM DELETE FROM table_name
WHERE ...


Data Types
Oracle
MySQL

Editors
Eclipse Data Tools Platform (DTP)
Eclipse SQL Explorer

How To Change @Author In Eclipse Templates

I have the problem of working with one project on multiple computers with different system user names.
This ends up in inconsisten javadoc comments where the tag @author get mixed up with all the system user names.

There are two solutions.


1. Change the system user name localy for the virtual machine Eclipse is running on.
This is the quick and dirty method, but it works globally.

Open your eclipse.ini file and add the following line after -vmargs:

-Duser.name = your name

The dirty part is that it overrides the user.name property which is used by some plugins that need to know the system user name you logged into the underlying OS. This could break them.



2. Change the templates in Eclipse
This hardcodes your user name into all the templates you want to change your name. But the templates are ex-, and importable. Define it once use it everywhere.
I know 2 templates with @author so far.

2.1 New Type Comment Template
Window -> Preferences -> Java -> Code Style -> Code Templates -> Comments -> Types
Press 'Edit...'
Change  @author ${user}  to  @author your name
If the Dialog is closed you can check the check box 'automatically add comments for new methods and types'.

Here you have the option to configure project specific templates. There is a link in the upper right corner 'Configure Project Specific Settings'.

2.2 @author Template
Window -> Preferences -> Java -> Editor -> Templates
Select in the list the template with the name '@author' .
Press 'Edit...'
Change  @author ${user}  to  @author your name

Tuesday, September 27, 2011

JFace: A Viewer For Composites

JFace comes along with these nice Viewers. They build up the next layer ontop of the standard widgets. You are setting you business objects into a Viewer and it knows from its Label- and ContentProvider how to handle them.

A Table got its TableViewer a Tree its TreeViewer, ...
Wouldn't it bee nice to have such a Viewer for Composites?
You stuff you business object into it like a Person object and it knows how to display it. The next time you stuff a Project object into it and you see an UI representation of it.

If you like the idea, have a look at my classes and read further.
Viewer:                CompositeViewer
ContentProvider:  ICompositeProvider
LabelProvider:     Your Composites that draws the UI for the business object.
                            They have to implement the interface IInput or extends the
                            class CompositeInput.


How to use the CompositeViewer:

Instantiate it and give it a size, because it is empty.
 compViewer = new CompositeViewer(parent, SWT.BORDER);  
 compViewer.setCompositeProvider(new CompositeProviderPersonProject());  
 compViewer.setLayoutData(createGridDataForInputFields(200, 200));  

The ICompositeContentProvider implementation looks like this.
 public class CompositeProviderPersonProject implements ICompositeProvider {  
      @Override  
      public Class<?> getCompositeClass(Object o) {  
           if(o.getClass() == Person.class) {  
                return CompositePerson.class;  
           }  
           if(o.getClass() == Project.class) {  
                return CompositeProject.class;  
           }  
           return null;  
      }  
 }  

If you place a Person object.
 compViewer.setInput(new Person("strange", "optics", 35, 'M'));  


If you place a Project object.
 compViewer.setInput(new Project("Strangewt", "More programming convinience!"));  

Monday, September 26, 2011

SWT: More Convenience Please

Are you bored like me writing tons of code for simple formular oriented UI's?
SWT forces us to write more lines of code and specify more parameters than necessary.
Here is one simple example:

 Label label = new Label(parent, SWT.NONE);  
 label.setText("labelname");  

First questions that come up are:

  • Why do I have to specify allways the style parameter when it is in 99.9% of all cases SWT.NONE.
  • Why do I have to create a reference 'label' and set the text of the Label in a second line and not in the constructor.

The guys from SWT missed out the chance to make there classes convenient with one or two more constructors for every widget. Next code snippet shows the minimum constructor for the Label class.

 new Label(parent, "labelname");  

So we have to do something. A convenient method is the answer. Look how efficient the code is. We save more than half of the key strokes.
The idea is actually using the factory method pattern from the Gof (Gang of Four) in a not so narrow sense.

 createLabel(parent, "labelname");  



Here a more complex example. This is a typical task for the UI. A label and text field, multiline textfield where the label is on top of its column and another multiline textfield with two labels. First could be the name an the second an explanation.


The usual code:
 Group group0 = new Group(parent, SWT.NONE);  
 group0.setText("group0");  
 group0.setLayout( new GridLayout(2, false));  
 group0.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
  
 Label label0 = new Label(group0, SWT.NONE);  
 label0.setText("label0");  
 Text text0 = new Text(group0, SWT.BORDER);  
 text0.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
  
 Label label1 = new Label(group0, SWT.NONE);  
 label1.setText("label1");  
 label1.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));  
 Text text1 = new Text(group0, SWT.V_SCROLL | SWT.MULTI | SWT.WRAP | SWT.BORDER);  
 GridData gd1 = new GridData(SWT.FILL, SWT.CENTER, true, false);  
 gd1.heightHint = 50;  
 text1.setLayoutData(gd1);
  
 Label label2 = new Label(group0, SWT.NONE);  
 label2.setText("label2");  
 label2.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));  
 Text text2 = new Text(group0, SWT.V_SCROLL | SWT.MULTI | SWT.WRAP | SWT.BORDER);  
 GridData gd2 = new GridData(SWT.FILL, SWT.CENTER, true, false);  
 gd2.heightHint = 50;  
 gd2.verticalSpan = 2;  
 text2.setLayoutData(gd2);  
 Label label3 = new Label(group0, SWT.NONE);  
 label3.setText("label3");  
 label3.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));  

The convenient code:
 Group group1 = createGroup(parent, "group1", 2);
  
 createLabel(group1, "label0");  
 createText(group1);  
 
 createLabelTop(group1, "label1");  
 createTextField(group1, 0, 50);  
 
 createLabelTop(group1, "label2");  
 createTextField(group1, 0, 50, 1, 2);  
 createLabelTop(group1, "label3");  



For this purpose I created CompositeBase and GroupBase classes to extend from. If you just want to have the create methods use the WidgetHelper.
The WidgetHelper implements everything as static methods. With a static import for this class, you don't have to prefix every method with the class name.

 import static org.strangewt.widgets.WidgetHelper.*;  

But be aware, Eclipse breaks down a static import when using 'organise imports' Ctrl + Shift + O.
To fix this problem read this.

Friday, September 23, 2011

Eclipse RCP: All My Registries

Using SWT you have to care about your operating system resources such as fonts, colors and images.
Fortunately JFace provides you with a set of registries, for every resource one FontRegistry, ColorRegistry and ImageRegistry. The resources are created lazily when they get accessed and are disposed automatically when the display is disposed.
There are global registries but to avoid conflicting keys you should use private instances.

Here one Example of the usage of an ImageRegistry:
 ImageRegistry imgReg = new ImageRegistry();  
 imgReg.put("avatar", ImageDescriptor.createFromURL(  
    FileLocator.find(bundle, new Path("icons/avatar.jpg"), null)));  
 Image avatar = imgReg.get("avatar");  

You have to instantiate the registry locally, if you want to use it somewhere else you have to make a singleton for it.
The metod put(String key, ImageDescriptor descriptor) takes an ImageDescriptor as an argument, but your image files are normally located in your plugin. You have to get the bundle for the location.
Lot of stuff for just a picture.

Wouldn't it be more easy to have just one singleton for all the registries with lots of convenient methods that take care of alle the specialities like the location of your files?

Have a look at my Registries class:
 package org.strangewt.jface.resource;
/**  
  * Manages all Registries from JFace. <br>  
  * - ColorRegistry <br>  
  * - FontRegistry <br>  
  * - ImageRegistry <br>  
  * First use must be after the <code>Display</code> has been created, this can  
  * be usually done in the method <code>IApplication.start()</code> of the  
  * RCP-Bundles. Afterwards you can set up all your colors, fonts and images by  
  * passing over a derived Object from the <code>IRegistriesConfiguration</code>  
  * interface.  
  */  
 public class Registries {  
      protected static ColorRegistry colorReg;  
      protected static FontRegistry fontReg;  
      protected static ImageRegistry imgReg;  
      private static Bundle bundle;  
      private static Registries instance;  
      private Registries() {  
           colorReg = new ColorRegistry();  
           fontReg = new FontRegistry();  
           imgReg = new ImageRegistry();  
      }  
      /**  
       * Is used to determine the root folder of the application. Get the bundle  
       * from <code>Activator.getDefault().getBundle()</code>  
       */  
      public static Registries getInstance(Bundle bundle) {  
           Registries.bundle = bundle;  
           return getInstance();  
      }  
      public static Registries getInstance() {  
           if (instance == null) {  
                instance = new Registries();  
           }  
           return instance;  
      }  
      public Registries init(IRegistriesConfiguration configurator) {  
           configurator.configure(this);  
           return this;  
      }  
      /**  
       * @param path  
       *      for example icons/background.jpg  
       */  
      public void putImage(String key, String path) {  
           imgReg.put(key, ImageDescriptor.createFromURL(FileLocator.find(bundle,  
                     new Path(path), null)));  
      }  
      public void putImage(String key, Image image) {  
           imgReg.put(key, image);  
      }  
      public Image getImage(String key) {  
           return imgReg.get(key);  
      }  
      public ImageDescriptor getImageDescriptor(String key) {  
           return imgReg.getDescriptor(key);  
      }  
      public void putColor(String symbolicName, int red, int green, int blue) {
           colorReg.put(symbolicName, new RGB(red, green, blue));
      }
      public void putColor(String symbolicName, RGB colorData) {  
           colorReg.put(symbolicName, colorData);  
      }  
      /**  
       * @param hexColorStr  
       *      "00FF00"  
       */  
      public void putColor(String symbolicName, String hexColorStr) {  
           Integer colorValue = Integer.parseInt(hexColorStr, 16);  
           int red = (colorValue & 0xFF0000) >> 16;  
           int green = (colorValue & 0x00FF00) >> 8;  
           int blue = (colorValue & 0x0000FF);  
           putColor(symbolicName, new RGB(red, green, blue));  
      }  
      public Color getColor(String key) {  
           return colorReg.get(key);  
      }  
      /**  
       * Looks up the registry if there is a color for the key. If not, a new  
       * color is instanciated from the hex-representation.  
       */  
      public Color getColor(String key, String hexColorStr) {  
           Color color = colorReg.get(key);  
           if (color == null) {  
                putColor(key, hexColorStr);  
                color = colorReg.get(key);  
           }  
           return color;  
      }  
      public String getColorAsHex(String key) {  
           String hexColorStr = null;  
           Color color = getColor(key);  
           if (color != null) {  
                hexColorStr = toHex(color.getRGB());  
           }  
           return hexColorStr;  
      }  
      /**  
       * Converts the color of an <code>RGB</code> object into his hex values. <br>  
       * RGB(0,255,0) -> "00FF00"  
       */  
      public String toHex(RGB rgb) {  
           String hexColorStr = Integer.toHexString((rgb.red << 16)  
                     + (rgb.green << 8) + rgb.blue);  
           if (hexColorStr.length() == 4) {  
                hexColorStr = "00" + hexColorStr;  
           } else if (hexColorStr.length() == 2) {  
                hexColorStr = "0000" + hexColorStr;  
           }  
           return hexColorStr;  
      }  
      public void putFont(String symbolicName, FontData fontData) {
           fontReg.put(symbolicName, new FontData[] {fontData});
      }
      public void putFont(String symbolicName, FontData fontData[]) {  
           fontReg.put(symbolicName, fontData);  
      }  
      public Font getFont(String key) {  
           return fontReg.get(key);  
      }  
 }  

The configuration interface:
 package org.strangewt.jface.resource;  
 public interface IRegistriesConfiguration {  
      /**  
       * Gets passed over to the <code>Registries</code> where it is used to  
       * configure it self. Typical content: <br>  
       * mr.putColor("active element", new RGB(0, 255, 0)); <br>  
       * mr.putImage("dialog.header", "icons/background.jpg"); <br>  
       * mr.putFont("view.headline", new FontData[] { new FontData("Tahoma", 10,  
       * SWT.BOLD) });  
       */  
      public void configure(Registries registries);  
 }  

To use it, you have to derive the interface and define your resources. Look how easy it is to put an image into the registry.
 public class DemoRegistriesConfiguration implements IRegistriesConfiguration {  
      @Override  
      public void configure(Registries r) {  
           r.putColor("red", 255, 0, 0);  
           r.putImage("avatar", "/icons/avatar_strangeoptics.jpg");  
           r.putFont("courier_bold", new FontData("Courier",10,SWT.BOLD));  
      }  
 }  

Then you have to instantiat the Registries class. Best location in the Aplication.start() method right after the Display is created.

Display display = PlatformUI.createDisplay();  
Registries.getInstance(Activator.getDefault().getBundle()).init(
new DemoRegistriesConfiguration());  

Now you can use the image everywhere in your code.

 Registries.getInstance().getImage("avatar")  

The source code is available in my svn repository at google code.

Wednesday, September 21, 2011

How To Make An In-Page-Link

Linking from one web page to another is the fabric the web is made of. But sometimes this is to coars. Sometimes your informations are on just one page but heavily interrelated.
Thinking of a table of contents for a closed topic you don't want to split up into single pages because every chapter is in the size of a couple of lines.

The solution are in page links. There are two ways of doing this. The old anchor way or the new xhtml compliant way.

Old stuff first
This is not recommended anymore because not all browsers support the anchors anymore.
Place the anchor:
 <a name="headline1">This Is About Deprecated Anchors</a>  
Place the Link:
 <a href="#headline1">Anchors</a>  

New XHTML Compliant
Target:
 <div id="headline2">  
    The New Way To Code In-Page-Links  
 </div>  
Link:
 <a href="#headline2">the new way</a>  
Link from another web page:
 <a href="anotherwebpage.html#headline2">the new way</a>  

Getting Started With Android: Part3 - User Interface

In this part I will show you how the UI is build up.

You have two options, declarativ or programatic. Both have there benefits and drawbacks, in some cases it is impossible to declare it. But your first and preferred option should be the declarativ way.

Declarativ
Benefits:

  • Clean separation of logic and UI
  • Usage of the visual designer.

The UI is declared in a XML layout file. All the layout files of an application are stored in the res/layouts folder.
The default layout file that gets created for every new application is called main.xml.
Lets have a look into ours. If you double click on the file in the resource tree, the visual designer will open up. It takes some time but you have a nice graphical representation of it.


For the moment we are more interested in the plain xml. Switch the bottom tabs from 'Graphical Layout' to 'main.xml'.

 <?xml version="1.0" encoding="utf-8"?>  
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
   android:orientation="vertical"  
   android:layout_width="fill_parent"  
   android:layout_height="fill_parent"  
   >  
      <TextView   
        android:layout_width="fill_parent"   
        android:layout_height="wrap_content"   
        android:text="@string/hello"  
        />  
 </LinearLayout>  

Layouts
LinearLayout is a layout type. There are a lot of layout types like:

  • LinearLayout
  • RelativeLayout
  • FrameLayout
  • TableLayout

Layouts are containers for other Views that arange their child Views in different ways.

Views
TextView is a class that derives from View. Views are the basic classes for the UI. Even Layouts are derived from View.

  • TextView
  • ImageView
  • Button

To assign a layout to an Activity we have a look into our HelloWorldActivity from Part2. We find the callback method onCreate(). That is called from the Activity at the time it changes from the state Create to Running. This happens exactly once in the life cycle of every Activity.
Here we initialize our Activity.
In line 4 we tell the Activity that it should inflate from the resources the R.layouts.main layout.

1:  @Override  
2:  public void onCreate(Bundle savedInstanceState) {  
3:      super.onCreate(savedInstanceState);  
4:      setContentView(R.layout.main);  
5:  }  
To get access to the inflated Views the View must have an id. Go to the layout and add android:id="@+id/text1" to the tag of the TextView.

 <TextView android:id="@+id/text1"  
        android:layout_width="fill_parent"   
        android:layout_height="wrap_content"   
        android:text="@string/hello"  
        />  

Now you can use the method findViewById() to obtain the instance of your desired view.

 TextView textView = (TextView)findViewById(R.id.text1);  


Programatic
Benefits:

  • Allows you to build up dynamic UIs like a month view of a callendar

The same UI as above looks coded like this.

   public void onCreate(Bundle savedInstanceState) {  
     super.onCreate(savedInstanceState);  
 //    setContentView(R.layout.main);  

     LinearLayout layout = new LinearLayout(this);  
     layout.setLayoutParams(new LayoutParams(
          LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));  

     TextView textView = new TextView(this);  
     textView.setText("Hello World, HelloWorldActivity!");  
     textView.setLayoutParams(new LayoutParams(
          LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));  

     layout.addView(textView);  
     setContentView(layout);  
   }  

As you can see, the layout parameter are lengthy and the code completion for the parameters that you have in the xml layout file is missing.

Tuesday, September 20, 2011

How To Open Chrome With A Set Of Tabs

If you are like me you want to open your browser and getting a specific task done. Here are two examples.
1. I want to research on some topics and aggregate the result in a document. Therefore I need open a tab with wikipedia and one with google docs.
2. Or I want to write a new post in my blog. Therefore I need 4 tabs one with the dashboard of the blog, one for the blog itself, google docs for my research/outline and a dictionary.

Wouldn't it be nice to open up the browser with the tabs for a special task opend? No boring searching and clicking all the bookmarks!

The solution ist to start chrome with the parameter --homepage <url>

To do this, go to the chrom icon on your desktop. Right click and make a short cut. Rename it like your task, in my case 'blogging'.


Right click on one and choose preferences.


Go to the property 'target' and add at the end of the line the argument.
It should look like this:

C:\Users\work\AppData\Local\Google\Chrome\Application\chrome.exe --homepage http://www.blogger.com/stats2.g?blogID=2438166163329174006 http://strangeoptics.blogspot.com/ https://docs.google.com/?hl=de&tab=wo&authuser=0#home http://dict.leo.org/ende?lp=ende&la

You can add as much websites as you want, separated by a blank.

Links to the guys from Chrom:
The Chromium Projects - Run Chromium with switches
List of chromium command line switches

Potato-Egg-Salat

There are a million of potato salat recipes but this is one of my favorites.












Ingrediens: 2 Persons
250 g potatoes, sliced
2 green onion, sliced
2 pickeld cucumber, diced
6 radish, diced
2 boiled eggs, diced
some chive

Dressing:
1 Tbsp oil
150 g yoghurt
1 Tbsp lemon juice
1 Tbsp vinegar or pickeld cucumber juice
1 teaspoon sugar
salt, pepper

Preparation:
Put all ingrediens together in a bowl.
Mix all dressing ingidiends together and pour it over the salat and stir it.
Leave it in the fridge for half an houre until the dressing is soaked in.
Serve cold.


Print this post

Saturday, September 17, 2011

Best Of Short Cuts For Eclipse: Ctrl + Shift + L

These are my most important short cuts after 8 years of busy java coding.
Increase your productivity and impress your coworker.

I will show you the predefined short cuts, how to define your own and the way to reuse them in all your eclipse IDE's.



Predefined Bindings
You can find an official overview of the predifined short cuts by clicking under Help -> Key Assist or more apropriate with the following short cut Ctrl + Shift + L.

Changing the code:
Ctrl + D Delete Line
Ctrl + Alt + Up/Down Duplicate Lines
Alt + Up/Down Move Lines
Alt + Shift + R Rename - Refactoring
Ctrl + Z Undo
Ctrl + Y Redo
Ctrl + Shift + O Organise Imports
Ctrl + Shift + F Format
Ctrl + Space Content Assist
Ctrl + 7 Toggle Comment

Navigating the code:
F3 Open Declaration
Ctrl + mouse over Open Declaration, (Super) Implementation
Ctrl+T Quick Hierarchy (like above without a mouse)
F4 Type Hierarchy
Ctrl + Shift + T Open Type
Ctrl + K Find Next
Ctrl + Shift + K Find Previous
Alt + Left Backward History
Alt + Right Foreward History
Ctrl + Alt + H Open Call Hierachy

Organise Imports
Be aware, it removes static imports like the handy one for junit
import static org.junit.Assert.*;
There are two solutions:
1. You can fix this by going to Window -> Preferences -> Java -> Code Style -> Organize Imports
There you change in the field 'Number of static imports needed for .*' the number of 99 to 1.
This is the number of static import statements that are allowed for static members of the same type before type is used.
2. Another way is to use the favorites of the content assist function.
Go to Window -> Preferences -> Java -> Editor -> Content Assist -> Favorites.
Enter a 'New Type...' like 'org.junit.Assert.*'.
When you use the next time the auto complete Ctrl + Space the it will propose those static members even if the import is missing.

Format
You can find the configuration under Window -> Preferences -> Java -> Code Style -> Formatter



Custom Bindings
Go to Window -> Preferences -> General -> Keys
Alt + W P 'keys' down down

Here you can look up all available commands and their key binding. Look at the command 'Activate Editor' it has a binding for the key 'F12'. One line above, the command 'About' doesn't got one.

We will add one binding step by step. In the filter field 'type filter text' type in 'generate'. Now the list shows 5 items. Select the command 'Generate Getters and Setters'.
Go to the 'Binding' field. As you start to press a button it will come up there with a trailing +.
If you press a combination of keys they get conncatenated in the order you pressed them with the +.
For generating getters and setters I would naturaly choose Ctrl+Shift+G.
This will lead to a conflict with the command 'References in Workspace'. Resolve it by choosing another key combination : Ctrl+Shift+S

Suggested:
Ctrl+Shift+S Generate Getters an Setters
Ctrl+Shift+E Show In (Package Explorer)



Reuse

Go to File -> Export... -> General -> Preferences.
Choose "Export all" or just "Keys Preferences".
To import them works the same way.

Friday, September 16, 2011

Getting Started With Android: Part2 - Hello World

In this part I show you how to create a project and to start it either in the emulator or on your phone.

Click on the new button in the upper left corner.


In the next dialog choose the 'Android Project' wizard.


Now you have to give the Project a name. If you want to use the package name like I do usually, have a look at the Activity name. It is automaticaly derived from the project name but with the first letter in high case.
org.android.helloworld  will get Org.android.helloworldActivity.
Fix it by hand or don't use the package as the project name.




The resulting project structure looks like the following picture. I will tell you later about it.


Hit the debugg or run button.


The emulator starts. 


Drag the locker icon to the right. The screen shows your hello world app.


Viola your are done.

Wednesday, September 14, 2011

Cooking: Chili Sin Carne

This is the vegetarian version of chili con carne.

Ingridiends:
150 g unripe spelt grains or just bulgur
600 ml vegetable stock
3 onions
3 cloves garlic
800 g canned tomatos
300 g canned corn
300 g canned kidney beans
3 Tbsp oil
1 teaspoon paprika powder
2 teaspoons tomato purée
2 teaspoons cummin, grounded
1 chili
2 capsicum


Cook the unripe spelt grains with the vegetable stock for about 30 minutes.
Pour the stock into a bowl and put it aside.

Chop the onions and the garlic into small pieces.
Heat oil in a deep pan and sauté the onions until soft add the garlic and cook for 2 more minutes.
Dice the capsicum mean while.
Add the grains and fry it for a short time.
Now add the rest, tomato purée, paprika powder, cummin, tomatos, corn and the beans.
Simmer it for 20 minutes.
Add as much of the stock as you like.


Monday, September 12, 2011

Java: Sorting objects by multiple attributes

The best way to do this is by using the ComparatorChain from the Apache Collections Framework.
You have to implement for every attribute a Comparator that you add to the Chain in the order you need. Now you can use the chain like a normal Comparator.
Here is how it looks like in code:
 package org.test.comparator;  
 public class Person {  
      public Person(String name, int age) {  
           this.name = name;  
           this.age = age;  
      }  
      public String name;  
      public Integer age;  
      @Override  
      public String toString() {  
           return "[" + name + "|" + age + "]";  
      }  
 }  

 package org.test.comparator;  
   
 import java.util.ArrayList;  
 import java.util.Collections;  
 import java.util.Comparator;  
 import java.util.List;  
   
 import org.apache.commons.collections.comparators.ComparatorChain;  
   
 public class TestComparatorChain {  
      public static void main(String[] args) {  
           List<Person> persons = new ArrayList<Person>();  
           persons.add(new Person("stan", 31));  
           persons.add(new Person("kyle", 22));  
           persons.add(new Person("stan", 11));  
           persons.add(new Person("kyle", 30));  
             
           Comparator<Person> comparatorName = new Comparator<Person>() {  
                @Override  
                public int compare(Person o1, Person o2) {  
                     return o1.name.compareToIgnoreCase(o2.name);  
                }  
           };  
             
           Comparator<Person> comparatorAge = new Comparator<Person>() {  
                @Override  
                public int compare(Person o1, Person o2) {  
                     return o1.age.compareTo(o2.age);  
                }  
           };  
             
           ComparatorChain chain = new ComparatorChain();  
           chain.addComparator(comparatorName);  
           chain.addComparator(comparatorAge);  
             
           System.out.println(persons);  
             
           Collections.sort(persons, chain);  
             
           System.out.println(persons);  
      }  
 }  

Resulting two lines:
[[stan|31], [kyle|22], [stan|11], [kyle|30]]
[[kyle|22], [kyle|30], [stan|11], [stan|31]]

The commons-collections-3.2.1.jar is with 575 kb quite big if you only want to use one class of it. Fortunately the class ComparatorChain has no dependencys to other classes of the collections library.
Download it from here and copy it into you project but keep the original package name org.apache.commons.collections.comparators, it helps you later to track back where the class came from.