środa, 11 listopada 2009

Android + Metawidget = Interesting cooperation

Dzisiejszy post bedzie w jezyku angielskim, bo to, co tu pokaze nie widzialo jeszcze swiatla dziennego, zatem chcialbym sie tym podzielic latwiej z wieksza iloscia zainteresowanych.
Nie bedzie to ani wprowadzenie do Androida. Duzo materialów jest w necie, dobre wprowadzenie zapewnia strona Android Developers
Nie bedzie to tez wprowadzenie do Metawidget - dokumentacje tego narzedzia jest naprawde dobra, a autor oferuje szybkie i porzadne wsparcie na forum.
Bedzie to case study na temat wprowadzania Metawidgeta do aplikacji na Androida - dostepne dla kazdego, nawet bez znajomosci zadnej z powyzszych technologii.

Lately I've been deep into researching Android as Java development platform. As I'm not really a 'UI-guy' I thought to try to use some tool to make all the dirty (no offence) UI job done. I decided to use Metawidget, which is able to generate views in multiple technologies, with Android among them.

Full of optimism I started creating my first application (It will be in the Android Market this year).
First I wanted a View for editing my 'Sleep' object.

public class Sleep1 {

public int id;
public Date goSleep;
public Date wakeUp;

}

I can hear the voices shouting "What?! public fields!? - haven't You heard about encapsulation?" or similiar. But don't listen to them. In my case these are irrelevant.

In metawidget to make Your object shown - You have to set it for inspection. Inspectors will come and check what actually is inside Your object and create some metadata. Then there will come a WidgetBuilder which will read the metadata and generate a View - dynamically.

In Android Activity I add

final AndroidMetawidget metawidget = (AndroidMetawidget) findViewById(R.id.sleepmetawidget);
metawidget.setToInspect(sleep);

and the result is:

Not very fantastic. Yes, metawidget needs some tuning, that's sure. Let's see what we can do! The order is other than expected, and date looks quite interesting but totally not useful. We would need a kind of 'picker' for the date. The reason why metawidget shows a EditText view for date is that the Date property is not required, therefore user has to have a way of non-specifying it, and it can be achieved by EditText

We will use metawidget's @UiHidden and @UiRequired annotations first .

public class Sleep2 {

@UiHidden
public int id;

@UiRequired
public Date goSleep;

@UiRequired
@UiComesAfter("goSleep")
public Date wakeUp;
}

which results in


I decided that user doesn't need the id attribute, and set the order of attributes (Maybe we could wish that order of fields methods in class would be the same in source and in class file? This would be good for Java I think, maybe submit it to project Coin?)

The @UiRequired is telling metawidget to show a 'picker' for date object, unfortunatelly it chooses a 'DatePicker' while I would need a 'time' precision for my attribute.
I would like the date should be presented at the top of the widget, and 'Date' attributes should be presented as an hour:minute. Because Metawidget is open for extension, and promotes 'Composition over Inheritance' I had to extend it a little.

1. Create annotation to tell metawidget to treat the date object as 'time'.
 
@Retention( RetentionPolicy.RUNTIME )
@Target( { ElementType.FIELD, ElementType.METHOD } )
public @interface UiAndroidTimeStyle { }

2. Create an Inspector object that will read the annotation and add metawidget information about desired behviour.

public class DateOrTimeInspector extends BaseObjectInspector {

@Override
protected Map<String, String> inspectProperty(Property property) throws Exception {

Map<String,String> attributes = CollectionUtils.newHashMap(1);

if ( property.getType().equals(Date.class)) {
if (property.isAnnotationPresent(UiAndroidTimeStyle.class)) {
attributes.put(InspectionResultConstants.TIME_STYLE,
InspectionResultConstants.TRUE);
}
}
return attributes;
}
}

3. Create an WidgetBuilder object that would actually read the metadata and create 'TimePicker' view.

package org.bartczak.metawidget;

import static org.metawidget.inspector.InspectionResultConstants.REQUIRED;
import static org.metawidget.inspector.InspectionResultConstants.TRUE;

import java.util.Calendar;
import java.util.Date;
import java.util.Map;

import org.metawidget.android.widget.AndroidMetawidget;
import org.metawidget.android.widget.AndroidValueAccessor;
import org.metawidget.inspector.InspectionResultConstants;
import org.metawidget.util.ClassUtils;
import org.metawidget.util.CollectionUtils;
import org.metawidget.util.WidgetBuilderUtils;
import org.metawidget.widgetbuilder.impl.BaseWidgetBuilder;

import android.text.method.DateKeyListener;
import android.view.View;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.TimePicker;
import android.widget.TimePicker.OnTimeChangedListener;

public class AndroidTimePickerWidgetBuilder extends BaseWidgetBuilder<View, AndroidMetawidget> implements AndroidValueAccessor {

@Override
protected View buildActiveWidget(
String elementName,
Map<String, String> attributes,
AndroidMetawidget metawidget) throws Exception {

String type = WidgetBuilderUtils.getActualClassOrType( attributes );

Class<?> clazz = ClassUtils.niceForName( type );

if ( Date.class.isAssignableFrom( clazz ) ) {
// Not-nullable dates can use a DatePicker or TimePicker
if ( TRUE.equals( attributes.get( REQUIRED ) ) ) {
if ( InspectionResultConstants.TRUE.equals(
attributes.get(InspectionResultConstants.TIME_STYLE))) {
TimePicker timePicker = new TimePicker(metawidget.getContext() );
timePicker.setIs24HourView(Boolean.TRUE);
timePicker.setOnTimeChangedListener(new OnTimeChangedListener() {

public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
updateValueForView(view);
}
});
return timePicker;
} else {
return new DatePicker( metawidget.getContext() );
}
}

return newEditTextField(metawidget);
}
return null;

}

private View newEditTextField(AndroidMetawidget metawidget) {
EditText editText = new EditText( metawidget.getContext() );
editText.setKeyListener( new DateKeyListener() );

return editText;
}

protected void updateValueForView(TimePicker view) {
values.put(view,getValueFromView(view));
}

Map<View,Object> values = CollectionUtils.newHashMap();

public Object getValue(View view) {
return values.get(view);
}

private Object getValueFromView(View view) {
if (view instanceof TimePicker) {
TimePicker timePicker = (TimePicker) view;
Calendar date = Calendar.getInstance();
date.set(Calendar.HOUR_OF_DAY, timePicker.getCurrentHour());
date.set(Calendar.MINUTE, timePicker.getCurrentMinute());
return date.getTime();
}
return null;
}

public boolean setValue(Object value, View view) {
if (view instanceof TimePicker) {
if (value instanceof Date) {
Calendar date = Calendar.getInstance();
date.setTime((Date) value);
TimePicker timePicker = (TimePicker) view;
timePicker.setCurrentHour(date.get(Calendar.HOUR_OF_DAY));
timePicker.setCurrentMinute(date.get(Calendar.MINUTE));
updateValueForView(timePicker);
return true;
}
}
return false;
}

}

This class is also able to handle the events on TimePicker so as it's not needed to use the 'Save' button.

4. Configure metawidget to use my object in a 'compose manner' with all previous functionality

<?xml version="1.0"?>
<metawidget xmlns="http://metawidget.org" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://metawidget.org http://metawidget.org/xsd/metawidget-1.0.xsd"
version="1.0">

<androidMetawidget xmlns="java:org.metawidget.android.widget">
<inspector>
<compositeInspector xmlns="java:org.metawidget.inspector.composite"
config="CompositeInspectorConfig">
<inspectors>
<array>
<metawidgetAnnotationInspector
xmlns="java:org.metawidget.inspector.annotation" />
<propertyTypeInspector
xmlns="java:org.metawidget.inspector.propertytype" />
<java5Inspector xmlns="java:org.metawidget.inspector.java5" />
<dateOrTimeInspector xmlns="java:org.bartczak.metawidget" />
</array>
</inspectors>
</compositeInspector>
</inspector>
<widgetBuilder>
<compositeWidgetBuilder xmlns="java:org.metawidget.widgetbuilder.composite"
config="CompositeWidgetBuilderConfig">
<widgetBuilders>
<array>
<androidTimePickerWidgetBuilder
xmlns="java:org.bartczak.metawidget" />
<androidWidgetBuilder
xmlns="java:org.metawidget.android.widget.widgetbuilder" />
</array>
</widgetBuilders>
</compositeWidgetBuilder>
</widgetBuilder>
</androidMetawidget>

</metawidget>

5. Use the metawidget

public class Sleep3 {

@UiHidden
public int id;

/**
* returns the date of sleep in the 'Day.Month' format
* @return
*/
@UiReadOnly
public String getDayOfSleep() {
return TimeUtil.inDayMonthFormat(goSleep);
}

@UiRequired
@UiAndroidTimeStyle
@UiComesAfter("dayOfSleep")
public Date goSleep;

@UiRequired
@UiAndroidTimeStyle
@UiComesAfter("goSleep")
public Date wakeUp;

}

Which results in


That looks quite OK now.

Let's apply some styles now. According to metawidget docs I configure to use my style in metawidget.xml:

<layout>
<tableLayout xmlns="java:org.metawidget.android.widget.layout"
config="LinearLayoutConfig">
<labelStyle><int>@org.bartczak.metawidgetshow:style/MyText</int></labelStyle>
<sectionStyle><int>@org.bartczak.metawidgetshow:style/MyText</int></sectionStyle>
</tableLayout>
</layout>

Where MyText is android style:

<style name="MyText">
<item name="android:textSize">26sp</item>
<item name="android:textColor">#0f0</item>
<item name="android:gravity">right|center_vertical</item>
</style>

and that is looking:


which is sort of what we could expect. Only color attribute has been used. What about others? Here I came into discussion with Richard Kennard on the metawidget forum and it was clear that it's not that easy in Android to apply a style programmatically. You can, however apply style attributes, one by one. Quick dive into metawidgets code into AndroidUtils.applyStyle, and I could see, that color is applied, so as textSize (although textSize isn't working?).
I wanted to make it done, so edited the code of applyStyle:

if ( view instanceof TextView ) {
attributes = metawidget.getContext().obtainStyledAttributes( style, new int[] { R.attr.textColor, R.attr.gravity } );
TextView textView = (TextView) view;

ColorStateList colors = attributes.getColorStateList( 0 );

if ( colors != null )
textView.setTextColor( colors );

int gravity = attributes.getInteger(1, BOGUS_DEFAULT);

if ( gravity!= BOGUS_DEFAULT) {
textView.setGravity(gravity);
}

attributes = metawidget.getContext().obtainStyledAttributes(style, new int[] {android.R.attr.textSize});

float textSize = attributes.getDimensionPixelSize(0, BOGUS_DEFAULT );
if ( textSize != BOGUS_DEFAULT )
textView.setTextSize( textSize );
}

and used my version of the class.

My style attributes are now applied! It looks like 'textSize' attribute has to be obtained in seperate call obtainStyledAttributes()! I don't know why, maybe I should create a small project and submit that bug to google.

Last thing I wanted to do was i18n. Android has support for that, but it is not integrated in metawidget. The default way for metawidget is to specify a label, and first it's trying to look for a key in resources with that name. In Android in runtime resources are identified by int keys in the magic 'R' class, so this would be very ineffective if we would have to translate the label into 'int', lookup the int value by reflection from the 'R' class, and get the real text which is represented by this int key.
But the int keys in 'R' class looks like this:

public final class R {
public static final class attr {
}
public static final class drawable {
public static final int icon=0x7f020000;
}
public static final class id {
public static final int sleepmetawidget=0x7f070000;
}
public static final class layout {
public static final int main=0x7f030000;
}
public static final class raw {
public static final int metawidget=0x7f040000;
}
public static final class string {
public static final int app_name=0x7f050000;
public static final int dayOfSleepLabel=0x7f050006;
public static final int goSleepLabel=0x7f050007;
public static final int wakeUpLabel=0x7f050008;
}
public static final class style {
public static final int MyText=0x7f060000;
}
}

These are constants compile-time, and could be used as annotation parameter. So i have prepared another extension to metawidget!

1. Annotation to specify a key for label:

@Retention( RetentionPolicy.RUNTIME )
@Target( { ElementType.FIELD, ElementType.METHOD } )
public @interface UiAndroidLabelKey {
int value();
}

2. It's usage

public class Sleep4 {

@UiHidden
public int id;

/**
* returns the date of sleep in the 'Day.Month' format
* @return
*/
@UiReadOnly
@UiAndroidLabelKey(R.string.dayOfSleepLabel)
public String getDayOfSleep() {
return TimeUtil.inDayMonthFormat(goSleep);
}

@UiRequired
@UiAndroidTimeStyle
@UiComesAfter("dayOfSleep")
@UiAndroidLabelKey(R.string.goSleepLabel)
public Date goSleep;

@UiRequired
@UiAndroidTimeStyle
@UiComesAfter("goSleep")
@UiAndroidLabelKey(R.string.wakeUpLabel)
public Date wakeUp;

}

3. Inspector to read the annotation

public class AndroidLabelKeyInspector extends BaseObjectInspector {

@Override
protected Map<String, String> inspectProperty(Property property) throws Exception {

Map<String, String> attributes = CollectionUtils.newHashMap(1);

if (property.isAnnotationPresent(UiAndroidLabelKey.class)) {
int annotationValue = property.getAnnotation(UiAndroidLabelKey.class).value();
attributes.put(AndroidMetawidget.LABEL_KEY, "" + annotationValue);
}

return attributes;
}

}

4. configure metawidget to use the inspector

<inspector>
<compositeInspector xmlns="java:org.metawidget.inspector.composite"
config="CompositeInspectorConfig">
<inspectors>
<array>
<metawidgetAnnotationInspector
xmlns="java:org.metawidget.inspector.annotation" />
<propertyTypeInspector
xmlns="java:org.metawidget.inspector.propertytype" />
<java5Inspector xmlns="java:org.metawidget.inspector.java5" />
<dateOrTimeInspector xmlns="java:org.bartczak.metawidget" />
<androidLabelKeyInspector xmlns="java:org.bartczak.metawidget" />
</array>
</inspectors>
</compositeInspector>
</inspector>

5. modification of AndroidMetawidget class
addition in getLabelString() to use new metadata

String labelIntKey = attributes.get( LABEL_KEY );
if ( labelIntKey!= null) {

Integer realKey = Integer.valueOf(labelIntKey);

return getLocalizedLabelByIntKey(realKey);

}

and simple method to obtain label from Android

public String getLocalizedLabelByIntKey( int key ) {
return getContext().getResources().getText(key).toString();
}

My strings looked like this (strings.xml)

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Metawidget showcase</string>
<string name="dayOfSleepLabel">Dzien</string>
<string name="goSleepLabel">Zasniecie</string>
<string name="wakeUpLabel">Pobudka</string>
</resources>

and application:


Yes! That's what I wanted. Labels in polish :) However if You supply multiple languages, most suitable will be chosen in runtime by Android.

Summary: In this article I wanted to show You that it is not that hard to fix or extend Metawidget. So go on - use it, and when it can't handle something - write it! I hope my contribution will be in some parts accepted by Richard Kennard, metawidget's author.

Using metawidget is fun, with Android - even bigger. I treat it as a kind of training in developing interesting software, and in creating libraries and development tools. Creating a framework for Android is also a good way to understand Android better.

All code written here is licensed on LGPL license, same as Metawidget itself.

niedziela, 31 maja 2009

Jeszcze bardziej kontekstowe hiperłącza!

Czy hiperłącza nie powinny być jeszcze potężniejszym mechanizmem? Weźmy pod uwagę np. kawałek zdania "40% budżetu UE", które w hipertekście może wyglądać tak:


40% budżetu UE


albo tak:


40% budżetu UE


Czyż informacja nie byłaby bardziej pełna, gdyby móc wprowadzić obie metody hiperłączy w jednym hipertekście? Czasem człowiek jest zainteresowany czym jest budżet, czasem tym, czym jest UE, a czasem chce się dowiedzieć o tej kombinacji, czyli budżecie UE.

Zapewne problem był już gdzieś opisywany, ale chyba tylko naukowo (więc posiada trudną do odnalezienia nazwę), bo nic nie słyszałem, by istniała możliwość takiego linkowania w HTMLU. Przydatny dodatek? Przyjmie się?

czwartek, 28 maja 2009

Latarnik wyborczy

Wskazuje dokąd płynąć, gdy zgubiliśmy kurs ;-)

Latarnik wyborczy to serwis internetowy, w którym możemy odpowiedź na serię kluczowych pytań dotyczących poglądów na temat współczesnej europy - a otrzymamy odpowiedź na pytanie do którego komitetu nam najbliżej. Jeśli nie wiesz dokąd popłynąć 7 czerwca - sprawdź tu.

Myślałem kiedyś o stworzeniu serwisu społecznościowego podobnego do latarnika, gdzie każdy mógłby wprowadzić swoje poglądy na najważniejsze sprawy w społeczeństwie a serwis umożliwiałby szukanie osób o podobnych poglądach, dyskusję, a także pokazywałby deklarowane w kampanii oraz realizowane działania różnych osób publicznych.

A potem już tylko krok do demokracji bezpośredniej, gdzie za pośrednictwem bezpiecznego :) serwisu będziemy mogli wypowiedzieć się na te tematy w sposób wiążący.

piątek, 22 maja 2009

Idealna zakładka

Pomysł małego racjonalizatora:

Typowa zakładka do książki ma jeden minus. Po otworzeniu książki na odpowiedniej stronie - nie wiesz, w którym miejscu skończyłeś ostatnio czytać!

Więc opracowałem projekt takiej zakładki, oto on:

Strona frontowa:


Druga strona zakładki powinna być biała

Zakładka ta musi być wykonana w wymiarze niewiele większym od połowy wysokości książki, by przy żadnej konfiguracji nie wystawała zbytnio.

Oczywiście ustawiamy taką zakładkę tak, by jej strona frontowa dotykała czytanej strony, a strzałki pokazywały czytany wiersz.

Czekam na wsparcie społeczności w zakresie wykonania bardziej atrakcyjnego projektu graficznego, puszczenia tego do druku i założenia zakładkowego biznesu ;-P

PS. Nie sprawdzi się przy e-książkach. do nich automatycznie zakładki zapamiętuje np. foxit reader - lekka alternatywa do czytania pdfów.

niedziela, 10 maja 2009

Wybory do PE 2009 - socjotechnika czy świadomy wybór

Niebawem w Polsce odbędą się wybory do Parlamentu Europejskiego.
Po raz pierwszy w moim życiu miałem przez moment ochotę zostawić to wszystko w diabły twierdząc, że to i tak nieistotne. Przez ostatnie 5 lat nie zauważyłem, by taki a nie inny wybór polskich parlamentarzystów wpłynął w znaczącym stopniu na działania Unii.

Postanowiłem jednak zagłosować, by wyrazić swoje zainteresowanie i wpływ. Zrobię to bardziej świadomie, niż wielu innych, bo
a) postanowiłem zapoznać się ze wszystkimi komitetami, które wystawiły kandydatów (nie ma wiele informacji w mass mediach na ten temat)
b) nie skończę na tym, jak wyglądają kandydaci, ani też co mówią w głównych wywiadach, tam gdzie to dla mnie istotne, dotrę do programu kandydatów

W wyborach do PE nie podoba mi się wiele rzeczy.
1) Metoda liczenia głosów - d'Hondta - preferuje duże ugrupowania.
2) Próg wyborczy aż 5% - eliminuje małe ugrupowania - Moim zdaniem powinien być radykalnie zmniejszony albo zniesiony na potrzeby tych wyborów - bo wszak w PE i tak są dziesiątki partii, więc nie można mówić o nierozdrabnianiu głosów. Skłonność do kompromisów jest kluczowa.
3) Nie wspominając o tym, że jestem zwolennikiem zmierzania ku demokracji bezpośredniej, więc tak ogromna instytucja jak PE (samych parlamentarzystów jest tu 700) mnie nie przekonuje. Wolałbym zostawić więcej rzeczy nieuregulowanych i/lub w rękach ludzi.
4) Stosunkowo mała ilość merytorycznej dyskusji, za to masa wytykania sobie błędów i minusów.

Mimo to zachęcam do wzięcia udziału w wyborach. Nie tylko "pójść i wrzucić kartkę z tym samym znaczkiem, co na ostatnich wyborach", ale by trochę głębiej przyjrzeć się Unii Europejskiej, a także naszej scenie politycznej.
Nawet jeśli uważacie, że 'wszyscy kradną' lub coś podobnego, to może zmienicie zdanie, gdy dowiecie się, że są jeszcze inne, alternatywne partie/stowarzyszenia, które mają jasny program i mogą być warte Twojego głosu. Może warto zbadać listę komitetów i zapoznać się z ich programem. To będzie bardziej sumienny wybór.

Jest też pewna forma, którą osobiście popieram, ale mało kto się na nią decyduje (nie znam nikogo jak dotąd) - która mówi 'Tak' dla demokracji i 'Nie' dla wszystkich kandydatów - tj. pójść na wybory i skreślić całą kartkę - głos policzony, lecz nieważny.

A jeśli nie pójdziesz na wybory, to nie masz podstaw do komentowania sytuacji politycznej ;-)

sobota, 11 kwietnia 2009

Istota wolnego rynku jest jego problemem?

Wydaje mi się, że jednym z największych problemów/minusów wolnego rynku jest nadmierne nagradzanie najlepszych graczy. Najlepsi robią się tak dobrzy, że zjadają tych mniejszych i robią się jeszcze więksi i lepsi, aż w końcu są tacy duzi, że na rynku panuje pogląd 'too big to fail' czyli są zbyt duzi, by upaść. W związku z tym ryzyko współpracy z nimi można zminimalizować, co staje się problemem, gdy ta duża instytucja faktycznie ma problemy, bo wtedy wielu wydaje się i jest na rękę nie pozwolić jej upaść, co w ostatnich miesiącach widzieliśmy wielokrotnie.

Kilka dużych instytucji w jednej branży daje nam oligopol, co jest bardzo częstym przypadkiem dziś. Wszak wiadomo, że konkurencja męczy, i na potajemnym układzie cen można sporo zaoszczędzić. Wbrew urzędom antymonopolowym. Zresztą regulacje antymonopolowe dość długo nie będą w stanie poradzić sobie z taką sytuacją wystarczająco. Prawo dotyczy jednego lub grupy państw, a korporacje są ogólnoświatowe.

Zresztą antymonopol nie istnieje wszędzie, weźmy największy kartel świata : OPEC jawnie ustalający limity wydobycia.

Ciekawe, czy przy okazji aktualnej sytuacji ekonomicznej zajdą jakieś zmiany i w tym kawałku rzeczywistości.

Teoria wolnego rynku trochę przypomina teorię ewolucji - wszak przetrwanie jest weryfikowane przez środowisko(ewolucja)/rynek(ekonomia). W przypadku ewolucji sprawa ma się tak dobrze, bo gatunków jest ogromnie dużo, no i jeśli coś jest nieprzystosowane do środowiska, w którym się znalazło to umiera. Nie jest ratowane, chyba że ma przyjaciół na tyle silnych. To oczyszcza 'geny' i pozostawia tylko te, które są w stanie wyprodukować osobniki zdolne przeżyć w danym otoczeniu.

Obecnie zarówno jeśli chodzi o ewolucję sprawa jest trochę zaburzona, bo
a) próbujemy sami decydować o genach potomków
b) opanowaliśmy całą planetę i niszczymy ją.
c) wytworzyliśmy własny byt, trochę abstrakcyjny - globalną gospodarkę i jesteśmy od niego w pełni zależni, a nie jest on idealny

Inspirowane komentarzem Big Papa z interesującego artykułu o zbyt dużych bankach, polecam!

czwartek, 9 kwietnia 2009

log4j++ czyli log5j - wygodne logowanie

Zawsze mnie denerwowało dbanie o wydajność logowania i tworzenie kodu typu:

[1]
 
if(l.isDebugEnabled()) {
l.debug("Zaloguj to i tamto " + costam.getCosInnego() + " oraz " + foo.bar());
}

gdyż oczywiście niepotrzebna konkatenacja Stringów na systemie produkcyjnym jest niedopuszczalna.

Strasznie to utrudnia pisanie kodu i go zaśmieca.

Jest jednak rozwiązanie - choćby log5j:

log5j problem [1] rozwiązuje poprzez szablon komunikatu:

 
log.debug( "This thing broke: %s due to bar: %s on this thing: %s", foo, bar, car );


I jeśli akurat nie mamy poziomu debug to nie konkatenacji Stringów nie następuje.

log5j Rozwiązuje także problem drugi ze zbyt barokowym stylem deklaracji logera:

[2]
 
private static final Logger log = Logger.getLogger( MyClass.class );

Skracając ją do postaci:

 
private static final Logger log = Logger.getLogger();


Jasne, że potężne maszynki template-owania kodu w nowoczesnych IDE pozwalają wygenerować zarówno dłuższą jak i krótszą wersję kwestii [2], ale im mniej literek w kodzie, tym lepiej :)

Seam Framework również rozwiązuje problemy [1] i [2] i to nawet lepiej!
Dokumentacja pokazuje jak Seam [1] względem log5j wprowadza swoją kontekstualność, czyli zastępuje wyrażenia EL wartościami z kontekstu Seama

 
log.debug( "This thing broke: #{foo} due to bar: #{bar} on this thing: #{car}");


a w przypadku [2] rozwiązanie jest jeszcze krótsze:

 
@Logger private Log log;


i nie musimy się martwić o static final, gdyż to Seam zapewnia, że nie utworzy więcej niż jednej instancji logera na wszystkie obiekty klasy, a ponadto nie potrzebujemy konstruktora ani fabryki. Wszystko automagicznie!

Mi takie rozwiązanie bardzo pasuje, choć nie każdy jest tak optymistyczny:

Ciekawe, czy log4j 2.0, który ma wykorzystywać zalety Javy 5 wprowadzi coś takiego do standardu.

PS. log5j to malutka nakładka na log4j, więc możecie bardzo łatwo dodać log5j do classpatha i używać skróconej formy w dowolnym projekcie, gdzie dziś standardem jest log4j :)