blog.smart-java.nl
Ordina J-Technologies – Java Blog

Archief ‘Java API’ categorie




Hibernate, lazy loading and inheritance

Door: gnoij, 8 March 2010

A common problem with typecasting a lazy loaded entity to its child is a ClassCastException. This exception occurs because the dynamic created proxy implements the baseclass and has no knowledge about its subclasses.

Suppose we have a class B which extends A and a class C which has class A as a member as shown below.

The following test will fail with a ClassCastException on the last line.

The methods save(), clearSession() and retrieve() are just helper methods which implement the Hibernate session methods save(), clear() and get().

A search on the Internet shows a couple of solutions for this problem.

  1. Using interfaces as a proxy in the hibernate mappings. This will result in accessing the object through its interface only so all methods must be exposed in the interface. I don’t want to expose every public or protected method in the interface which are not intended for use by external parties.
  2. Using the Visitor Pattern to access the correct childclass. This means that users of these objects must write a lot of code just to use some getters. I don’t want to burden someone else with a local Hibernate problem.
  3. Using ((HibernateProxy)object).getHibernateLazyInitializer().getImplementation() whenever a typecast of an object to its child class is needed.

All of the solutions mentioned above are not suitable for me so I decided on another solution which is a variant of solution 3.

With a slight modification of the method getA() in class C, exposing the HibernateProxy is avoided.

Now the test completes without failure.

This has to be done for every getter which can return a lazy loaded proxy.

And if you (like me) don’t want Hibernate code in your domain model, you can move this code to the persistence layer and use dependency injection to use it.

It’s still not an elegant solution (IMHO there isn’t one), but its the best usable for me.




java.util.Calendar.getActualMaximum returns strange results

Door: Peter Schuler, 20 January 2010

At the end of last year I encountered something odd in the java.util.Calendar. Now is odd behavior nothing to be surprised of in the Java Calendar but this particular oddness was really hard to spot.

I will therefore share it with you.

The code

Let’s first look a some code dealing with getting the last day of the month:

public class CalendarTest {
  public static void main(String[] args) {
    Calendar c = Calendar.getInstance();
    c.set(Calendar.MONTH, Calendar.FEBRUARY);
    int maxDayOfMonth = c.getActualMaximum(Calendar.DAY_OF_MONTH);

    System.out.println("last day of month = " + maxDayOfMonth);
  }
}

Please read the java doc for Calendar.getActualMaximum():

Returns the maximum value that the specified calendar field could have, given the time value of this Calendar. For example, the actual maximum value of the MONTH field is 12 in some years, and 13 in other years in the Hebrew calendar system.

So the code above can print two different values right …? 28 or 29 depending on the whether this year is a leap year. As you could have guessed that is another unexpected possibility. I can also print 31. Yes… really.

I all depends on the date on which this code is executed.

The problem is that Calendar.getInstance() will return a Calendar filled with the current date/time. Let’s assume the above code runs on the last day of January. The call to getInstance() will return 31-01-2009. The next step puts the month to February. This will result in a overflow as 31-02-2009 is invalid. Because of this Calendar will move the date to 3-03-2000. And March has 31 days.

There is a bug report (status: Closed, Not a Defect) in the SDN which told me that the observed behavior was correct. I was not the first person surprised by this and I’m guessing I will not be the last.

I was …

  1. … lucky I wrote a decent unit test.
  2. … lucky to be running said unit test om 31 December.
  3. … unlucky for having to work on the last day of the year.

Otherwise the bug I created would properly have slipped through the QA cycles and would have ended up in production. There it would only be visible on the last three days of every month if someone would specify a date in February.

What about lenient?
The Calendar.setLenient function does protect against invalid input. Let’s quote the the Calendar java doc about Leniency:

Leniency
Calendar has two modes for interpreting the calendar fields, lenient and non-lenient. When a Calendar is in lenient mode, it accepts a wider range of calendar field values than it produces. When a Calendar recomputes calendar field values for return by get(), all of the calendar fields are normalized. For example, a lenient GregorianCalendar interprets MONTH == JANUARY, DAY_OF_MONTH == 32 as February 1.

When a Calendar is in non-lenient mode, it throws an exception if there is any inconsistency in its calendar fields. For example, a GregorianCalendar always produces DAY_OF_MONTH values between 1 and the length of the month. A non-lenient GregorianCalendar throws an exception upon calculating its time or calendar field values if any out-of-range field value has been set.

So lenient does protect against the programmer/user creating a invalid date in a way that the following code will result in an exception:

    Calendar c = Calendar.getInstance();
    c.setLenient(false);
    c.set(Calendar.MONTH, Calendar.FEBRUARY);
    c.set(Calendar.DAY_OF_MONTH, 31);
    System.out.println(c.getTime());

But the exception is only thrown when c.getTime() is called. Calls to getActualMaximum() still work and return 31. So lenient is not useful here.

Lessons learned

The quick fix for this problem is to set the DAY_OF_MONTH to 1 (or any number between 1 and 28) before setting the month.

I also learned that Calendar.setLenient() will not protect you from this error. It will only stop you from getting an invalid Date. It does not protect against overflows.

Sould I use JODA time?

At the end of this post I have a question for you. I have no experience with JODA time always preferring to use the standard Date/Time API unless there was a problem. But perhaps I should reverse my views and use JODA time unless I’m not allowed too? What do you think … is it time to stop  using the default Calendar API and use JODA time instead? Or should I wait for Java 7 with JSR 310?




JPA Optimisitic locking versus Pessimistic locking

Door: Peter Schuler, 14 January 2010

As promised in my previous post I will blog some more about JPA and how to use it. In this post I will go into the locking features of JPA 2.0 including the new pessimistic lock options.

This post will:

  • introduce the new locking features of the JPA;
  • introduce both pessimistic locking and optimitic locking concepts;
  • give a quick recap about JPA versioning;
  • talk about the connection between locking and design;
  • and finally compare both locking strategies.

JPA 2.0 now supports Pessimistic Locking:

A great omission of JPA 1.0 was the lack of pessimistic locking. Therefore it was necessary to fall back on the support of the underlying implementation to use the JPA is situation where pessimistic locking was required. This can happen when JPA shares a database with another process which does not know or supports versioning based optimistic locking.

JPA 2.0 now supports the following locking modes:

  • OPTIMISTIC                                    (==READ in JPA 1.0)
  • OPTIMISTIC_FORCE_INCREMENT         (==WRITE in JPA 1.0)
  • PESSIMISTIC_READ
  • PESSIMISTIC_WRITE
  • PESSIMISTIC_FORCE_INCREMENT       (A hybrid of both strategies)
  • READ                                               (kept around for backward compatibility)
  • WRITE                                             (kept around for compatibility)

Object can be locked by using the find() or refresh() operation of the EntityManager. For example:

Order order = entityManager.find(Order.class, 10, LockModeType.PESSIMISTIC_READ);
 
SELECT ID, PRODUCT, AANTAL, VERSION, orderId FROM ORDER_TABLE WHERE (orderId = ?) FOR UPDATE.

As you can see the FOR UPDATE is added to the select query telling the database to get an exclusive lock on this selected row.

You can also use the lock() method on the entityManager and specify a Lock Mode on Queries (JPQL / Named and Criteria).

Now that we know how to unleash the power of pessimistic locking we need to learn how to use it well.

Locking Strategies: a quick recap….

Locking is a means to prevent data form becoming corrupted because two different processes are editing the same data. If we use locking correctly no two processes can edit (or if required) access the same data. Thus data can never be inconsistent.

As already mentioned there are two locking strategies: optimistic locking and pessimistic locking. I will describe both strategies and give an overview of their pro’s and con’s.

Pessimistic locking

This strategy is the standard locking provided by the database. It will protect data by limiting access to a single process. This is achieved by keeping track of all the currently active locks. If another process wants to access locked data it will have to wait until the other process releases the lock. Of course this introduces a whole range of potential errors like lock timeouts and deadlocks.

This locking strategy is called pessimistic because of the assumption that locking is always necessary to avoid corruption. Based on that assumption it introduces significant overhead in order to keep track of which process is assessing which data. Compare pessimistic locking to a traffic light. It will only allow vehicles to pass when it knows for sure that no one will be in the way.

Using pessimistic locking has some pro’s:

  • The database is in charge and protects your data. Independent from application logic.
  • A process or thread can only proceed if it has the right locks. Thus it is guaranteed that there will be no conflicts once the lock is acquired.
  • Processes are put on hold until they can acquire the lock. (This blessing can also be a curse because a process can overwrite data the moment the lock is released. This feels like a missing update but is technically the correct behaviour. But as long as you read and write in the same transction you’re data is never stale.)

No pro’s without con’s:

  • Keeping track of all those locks introduces significant overhead. Even if there is no data being accessed simultaneous the database still locks.
  • The locking can lead to deadlocks and lock time out. These errors are hard to recover from and take a long time before the calling process is informed.
  • Must be supported by the database.

So pessimistic locking depends on the database restricting access to data. But this comes at high overhead and hard-to-recover errors.

Optimistic locking

As pessimistic locking is embedded in the DBMS, optimistic locking is a strategy that by-passes the database. It will detect conflicts only when they occur. This is done introducing a version number to every table you want to protect. If you read data you will get the version number. If you alter the data you first check the version number again, and when holding the previous read value, update the record and increment the version number. If some one has “changed the data right from under you” you will see a different version number and know that there is a conflict. I will refer to the optimistic lock procedure as check&update.

This strategy is called optimistic because it never bothers to lock. It assumes that process will not bother each other until they do.

Using optimistic locking has some big pro’s:

  • There is no (at least very little) overhead involved in locking.
  • Optimistic locking is fast and easy to use, especially because it works implicitly. If you specify a @Version the upate&check will be performed automatically.
  • It’s very efficient.
  • It is database independent. No special features are required.

There are also some drawbacks:

  • It will only detect conflicts, not prevent them. When it occurs it’s the application that must resolve the conflict. For example by showing the user a diff or an option to override the current version in the database.
  • If a conflict occurs only one process is allowed to proceed. The others have their database transaction rolled back. This is far more expensive than waiting until you get the database lock.
  • It will only work if everyone accessing the database plays by the versioning rules. The database does not enforce it.
  • It can be considered ‘unfair’ as the process that writes the data first wins, opposed to the process that first acquired the lock.
  • Sometimes optimistic locking is not sufficient. Locking a complete table to protect against insert for example.

So optimistic locking depends on the calling processes to respect the versioning rules. This makes it possible to detect conflicts and eliminates the need to keep of all the locks and gives Optimistic locking a huge advantage.

However when conflicts occurs it is up to the application to patch things up.

JPA support for optimistic locking

JPA supports optimistic locking based on versioning right from the first release. All you need to do is declare an attribute of your class with a @Version annotation.

For example:

  @Entity
  public class Order {
 
      @id @GeneratedValue
      private Integer id;
 
      @Version
      private Integer version;
}

The above code will result in a Order table with a primary key and version column. JPA will check and update the version after every change to Order.

More on JPA versioning can be found here.

Lock scope.

At first glance versioning seems to be the preferred strategy. It’s easy to use and with little overhead. However there is one more aspect to take into account when dealing with locking. That is what I call ‘the lock scope’.

Versioning will only lock (check&update) records that were changed. Databases will only lock records you tell it to lock by doing a SELECT … FOR UPDATE. Both procedures prevent processes from corrupting the database. But it will not prevent breaking business rules!
Let’s look at the following example:

This is a typical Order->OrderLine example. Order has a set of OrderLines which keep a price and quantity for every single item in Order. Let’s assume that there is a business rule that the total amount of money of an Order must stay below $10.000. This is easily achieved by adding a check on order to make sure that every addition/alteration of OrderLines will not break this rule.
A problem occurs when another process comes in and adds OrderLines to an Order at the same time. No single process knows all OrderLines. To make the check work in a concurrent environment you need to make the OrderLine updates in a serial order. In other words: the Order needs to be locked before any additions can be made to OrderLines. This ensures that the business rule can be enforced.

The JPA can achieve this by using one of the two lock levels: OPTIMISTC_FORCE_INCREMENT or PESSIMITC_WRITE. Both will give you a exclusive lock to make sure no other process can edit the same data.

This example illustrates that there are situations in which you need to think ahead about locking. Both versioning and database locks won’t help you out-of-the-box . You need to determine the right lock scope. Determining the lock scope is a business question and needs to be defined based on the functional design and then translated to technical requirements.

Choosing a locking Strategy.

Ok .. now you know about the pro’s and con’s of both locking strategies. You know how to use them technically and you know you need to think about the lock scope. So which strategy is for winners?

As you probably have guessed there is no straightforward answer.

Optimistic locking has little overhead and is easy to use, especially because it works implicitly in JPA. But you need to make sure that everyone using the database uses the same versioning approach. It’s also more expensive in terms of conflict resolving.

Optimistic locking is the preferred strategy if:

  • You’re application has a private database.
  • All the applications using the database know and use versioning.
  • It is unlikely that there will be a lot of conflicts. (eg. Users editing the same data.)

Pessimistic locking will protect data on the database level. It will prevent conflicts by putting the process in a queue to wait for the lock. If there are a lot of collisions this gives a better change of more processes making it through. However having to keep a large lock administration involves a lot of overhead even if there a no conflicts.

So pessimistic locking it the preferred strategy if:

  • Other non-versionized processes will edit the data you need to lock.
  • You predict / see that there will be a lot of colissions.

For those among us unable to choose, JPA offers a hybrid solution. If you use the lock option PESSIMISTIC_FORCE_INCREMENT both Pessimitic and Optimisic locks are acquired at the same time. Offcourse you’re cutting of both your hands when using this option for every database call…. “Just to be sure .. “. You’ll end up with the bad from both locking strategies. But this hybird option can be a life saver when a particular table or operation must be protected at a database level and still has to participate in versionized transactions.

More information on locking

And don’t forget to think about the lock scope!

This is the second installment of my blogs about the JPA. Next time we’ll go into the new Criteria API of JPA 2.0.

  • Special thanks to Martijn Blankestijn for the Order example and Jouke Stoel for test reading.



JPA 2.0 … finally final

Door: Peter Schuler, 11 December 2009

Thursday 10 December the people from EclipseLink release version 2.0 of their Object Relation Mapping framework. Besides other improvements this release includes the reference implementation of JPA 2.0.  This means that the JPA 2.0 and JSR-317 are now final!

The second release of the Jave Persistence API adds a lot of new features to the JPA framework. The team responsible for this release now claims to serve 95% of the persistence needs of Java programmers. You can get the final version of JSR-317 or download the reference implementation on the eclipselink website. Two older blogs by Mike Keith introduces all the new features here and here.

Version 2.0 now has (among others) support for:

  • Increased locking possibilities;
  • Support for Criteria Queries and a MetaModel;
  • (A little) support for second level caching;
  • Basic Element and embeddable collections;
  • Orphan removals for sets;
  • Derived Identifiers (using @ManyToOne as past of your primary key);
  • Ordered list.

With these new features the Java Persistence API becomes even more useful. In the next  few weeks I will blog some more about the new features and go deeper into some of the new features and how to use them to your advantange.

So stay tuned.




JDK 7 language changes: DeVoxx poll

Door: Jan-Kees van Andel, 8 March 2009

Afgelopen DeVoxx is een stemming geweest waar de bezoekers konden stemmen op hun favoriete nieuwe JDK 7 features.
De grote wijzigingen zijn natuurlijk allang bepaald, zoals !closures (niet dus), JVM optimalisaties voor scripttalen (JSR 292) en Java Module System (JSR 294), maar voor JDK 7 willen ze ook een aantal kleinere wijzigingen faciliteren.
Stephen Colebourne heeft op zijn blog een mooie samenvatting van de poll geplaatst.
Zoals je kunt zien, zitten er 3 populaire tussen:

  1. Null handling
  2. Infer generics
  3. Multi catch

Properties is ook populair, maar deze komt sowieso niet in JDK 7.
Deze 3 populaire features zal ik even kort laten zien:

Null handling
Iedereen heeft weleens dergelijke code gezien:

if (myObject != null) {
    if (myObject.getProp() != null) {
        if (myObject.getProp().getProp() != null) {
            return myObject.getProp().getProp().getString();
        }
    }
}

Zou het niet relaxed zijn als je het zo kunt schrijven?

return myObject()?.getProp()?.getProp()?.getString();

Er zitten nog wat vergelijkbare constructies aan vast, zoals een aanpassing aan de ternary operator, zie Alex Millers blog.

Infer generics
Waarschijnlijk hebben jullie deze constructie ook weleens gezien? Zo nee, dan heb je waarschijnlijk nog geen JDK 5+ collections gebruikt. :)

List<String> list = new ArrayList<String>();

Dit is nog niet zo vervelend, maar wat dacht je van deze variant?

Map<String, AtomicReference<SoftReference<SomeObject>>> list = new ConcurrentHashMap<String, AtomicReference<SoftReference<SomeObject>>>();

Vrij lelijk als je het aan mij vraagt. En in de meeste gevallen is het niet eens nuttig, aangezien links en rechts van de assignment hetzelfde staat.
Wat dacht je hiervan?

Map<AtomicReference<SoftReference<SomeObject>>> list = new ConcurrentHashMap<>();

De haken aan de rechterkant geven aan dat de compiler de typespecificatie links van de assignment naar de rechterkant gekopieerd moet worden.
Zie wederom Alex Millers blog voor de details.

Ten slotte de Multi catch
Jullie hebben ook vast weleens dergelijke catch clausules gehad neem ik aan.

try {
    // Do something
} catch (Exception1 e) {
    LOG.error("Something horrible happened", e);
} catch (Exception2 e) {
    LOG.error("Something horrible happened", e);
} catch (Exception3 e) {
    LOG.error("Something horrible happened", e);
} catch (Exception4 e) {
    LOG.error("Something horrible happened", e);
}

“catch (Exception)” is niet netjes, hebben we allemaal geleerd. Voor je het weet, vang je onterecht een fout af. Maar al die code duplicatie zit je toch niet lekker. Vandaar een nieuw alternatief:

try {
    // Do something
} catch (Exception1 | Exception2 | Exception3 | Exception4 e) {
    LOG.error("Something horrible happened", e);
}

Dat is een stuk beter he? Vooral in combinatie met Safe Rethrow kan dit erg krachtig zijn.
Ik ben nog wel benieuwd naar de details, want wat is het type van “e”? Gaan ze op zoek naar een common supertype, of wordt het duck typing?
En weer een linkje naar Alex Miller.

Dusss…
Dit zijn maar een paar van de toevoegingen waar momenteel aan gewerkt wordt. Meer info over “Project Coin” op Joe Darcy’s blog.
En natuurlijk weer Alex Miller.




Spelen met Java Bluetooth

Door: Hedzer Westra, 21 February 2009

Bluetooth logo

De Bluetooth JSR-82 en de API daarbij zijn al enkele jaren oud (v1.1: 2002), maar voor mij was het een nieuwe. Hiermee kun je vanuit Java met bluetooth apparaten communiceren. Het leek me wel leuk om, al forenzend in de trein een eBook te lezen en tegelijkertijd te zien wie er allemaal elke dag met me meereist. Deze zelfde exercitie is natuurlijk ook uitstekend in de file of op vaste locaties uit te voeren…

Inmiddels hebben heel veel apparaten een bluetooth (‘bt’) module: GPS ontvangers, WiiMotes, smartphones, billboards, verkeerslichten, you name it. Elk ingeschakeld apparaat adverteert zijn nummer en meestal een (door de fabrikant of eigenaar ingestelde) naam. De leukste tot nu toe vond ik “smartföhn”, maar voornamelijk zie je de naam van de eigenaar, of het merk & type van het apparaat verschijnen. O, en laat ik vooral ‘Dennis Ordina’ niet ongenoemd laten! Welke collega met Nokia telefoon met bt adres 001A165A1686 zat op 4 februari om kwart voor zes in de intercity van Amsterdam Bijlmer naar Utrecht CS? :-) Als laatste vermelding: één telefoon had als naam het 06-nummer. Lijkt me niet handig, behalve als je wilt dat Jan en alleman je belt…

De naam Bluetooth komt overigens van de 10e-Eeuwse koning van Denemarken en Noorwegen: Harald Blåtand (niet te verwarren met Blauwbaard!). Het logo is afgeleid van de runentekens van zijn initialen.
De laatste bluetoothversie (uit 2007) is 2.1+EDR (3Mbps), en er wordt gewerkt aan een high speed variant die moderne WiFi-snelheden haalt. De radius is afhankelijk van de ‘class’ 100, 10 of 1 meter.

Begrippen

Eerst vuur ik enkele begrippen op je af uit de ‘bt’ wereld, waarna wat codevoorbeelden langskomen.

Protocol bluetooth is opgebouwd als OSI network stack van protocollen, waarvan de interessantste L2CAP (Logical Link Control and Adaptation Protocol), RFCOMM (Radio Frequency Communications) en OBEX (OBject EXchange) zijn. Het zijn op elkaar gestapelde transportprotocollen net zoals TCP en IP op elkaar gestapeld zijn. L2CAP is packet oriented en RFCOMM stream oriented. OBEX is session oriented.
Profiel interfacespecificatie; ruwweg een netwerkprotocol op applicatieniveau. Er zijn er zo’n 25 gedefinieerd; voorbeelden zijn FTP (file transfer), HID (muis/keyboard), OPP (vCards), BIP (plaatjes) en PAN (piconet). JSR-82 omvat alleen de basisprofielen GAP, SPP, SDAP, GOEP en de daarbij horende protocollen SDP (GAP+SDAP), OBEX (GOEP), RFCOMM (SPP) en L2CAP. Andere profielen moet je zelf uitprogrammeren!
Bt adres vergelijkbaar aan een Ethernet MAC-adres. Bestaat uit 12 hex digits, waarvan de eerste 6 verdeeld zijn onder de fabrikanten. Aan het adres kun je dus al zien of het bijvoorbeeld een Nokia of Motorola apparaat betreft!
Master net zoals er bij TCP/IP-verbindingen servers (daemons) zich registeren op een bekende poort en clients hiernaar connecten, is bt ook client/server georiënteerd; de server heet ‘master’ en registreert (publiceert) services. Dit vind je terug in bt UUIDs en URLs.
UUID uniek nummer om een service (gedefinieerd in een profiel) te identificeren, vergelijkbaar aan een server IP port number. In 3 lengtes: 4, 8 of 32 hex digits. Er bestaat een simpele conversie om 4/8-digit UUID’s naar volledige lengte te converteren: concateneren met het vaste nummer 00001000800000805F9B34FB. Enkele voorbeeldwaarden: OBEX File Transfer = 0×1106, Human Interface Device Profile (WiiMote!) = 0×1124.
BCC Bluetooth Control Centre; een GUI om je device, services & security te configureren, en te zoeken naar & pairen met andere devices. Meegeleverd met je bt driver.
URLs je maakt connecties met behulp van URLs in het bekende formaat. Server URLs bevatten altijd “localhost”, de service UUID en eventueel parameters zoals een naam. Client URLs bevatten het remote bt adres, het kanaal- of PSM [Protocol Service Multiplexer] nummer – vergelijkbaar met een client IP port number; is niet hetzelfde als een service UUID – en eventueel (security) parameters. Enkele voorbeelden:
o OBEX/GOEP
Server btgoep://localhost:ed495afe28ed11da94d900e08161165f
Client btgoep://00A3920B2C22:12
o RFCOMM/SPP
Server btspp://localhost:;name=Sample SPP Server
Client btspp://0050CD00321B:3;authenticate={true|false}; authorize={true|false};encrypt={true|false}
o L2CAP
Client btl2cap://0050CD00321B:1003; ReceiveMTU=512;TransmitMTU=512
Piconet mininetwerk van maximaal 8 bt devices – meer verbindingen ondersteunt bluetooth niet. Een netwerk van meerdere aan elkaar gekoppelde piconets heet een scatternet.
Service class elk bt device kan met zijn service class aangeven welke service types hij ondersteunt (bijv. positioning, networking of audio) en van welk (sub)type hij is (bijv. Computer/Laptop of Peripheral/Joystick).
Discovery scannen naar devices en/of services. Na discovery levert JSR-82 je URLs zodat je die niet zelf hoeft op te bouwen. Een bt device kan altijd (‘global’/’general’) discoverable zijn (GIAC), een beperkte tijd (‘limited’ – LIAC) of helemaal niet. Dit poor-mans beveiligingsmechanisme is bekend van WiFi routers. Ook daar is de fabrieksinstelling meestal niet de veiligste… Discovery is niet erg snel; een complete sweep van devices & hun services duurt afhankelijk van het aantal devices in bereik één tot meerdere minuten, dit onder andere omdat er geen parallelle service discoveries kunnen draaien.
Service record bij discovery van een service ontvang je een lijst van attributen met onder anderen een leesbare naam en kanaal (RFCOMM&OBEX)- of PSM (L2CAP) nummer. Helaas vullen niet alle bt devices dit service record exact volgens de specs.
Pairing pas nadat apparaten aan elkaar bekend zijn gemaakt middels een PIN-code (meestal 0000…) kan communicatie gestart worden. Helaas voor mijn tagger kan dit alleen via de BCC; de JSR-82 API voorziet er niet in om een PIN-code door te geven. Bij pairing wordt overigens ook een gedeelde 128b sleutel afgesproken, die daarna gebruikt wordt als autorisatie en/of encryptie aangezet worden. Dat laatste kan dan wel weer vanuit Java. Discovery is overigens wel mogelijk zonder te pairen; mijn tagger kan van elk device dat in bereik is en in bluetooth discovery mode staat (hetgeen bijna elke telefoon is, vanuit de winkel) de volgende attributen uitvragen: bt adres, naam, service class, services inclusief naam, UUID en URL. Als een pairing wel bestaat (in JSR-82 termen: het device is ‘trusted’), is het mogelijk om een connectie op te bouwen, bijv. simpelweg om de pingtijd te meten.

Codevoorbeelden

De betrokken Java packages zijn: javax.bluetooth, javax.obex en javax.microedition.io.
Wat betreft dat laatste package: JSR-82 heeft veel raakvlakken met J2ME (termen in het verlengde hiervan: MIDP, CLDC en CDC), wat logisch is omdat bluetooth in 1998 door Nokia bedacht is als draadloze communicatie van mobieltjes naar andere apparaten. Mocht je iets met J2ME & bt willen doen (laat me je resultaten weten!): telefoons kunnen zelf ook JSR-82 implementeren, maar niet alle telefoons met bt & Java doen dat! Zie de bluecove Wiki voor een lijst.

De API is vrij klein. De belangrijkste klassen zijn DiscoveryAgent, LocalDevice en RemoteDevice.

Twee problemen die ik ben tegengekomen met deze API:
1. Een afgrijselijke klasse is DataElement. Dit is een wrapper voor diverse soorten datatypes. Je zult er helaas mee moeten leven.
2. Als je alleen het bt adres van een device weet, dan moet je eerst discoveren. Je kunt namelijk niet op basis van dit adres een RemoteDevice (laten) instantiëren. Maar als je een URL kent kun je wel meteen connecten! Vreemd…

Nu volgen enkele zeer kleine & korte codevoorbeelden – check voor enkele compleet werkende applicaties de Eclipse workspace op de Ordina Wiki. (Helaas zijn de syntax highlighting & tabs verloren gegaan bij kopiëren vanuit Word naar WordPress…)

// setup: get local device & discovery agent
LocalDevice localDevice = LocalDevice.getLocalDevice();
DiscoveryAgent discoveryAgent = localDevice.getDiscoveryAgent();

// start global device inquiry
MyDiscoveryListener discoveryListener = new MyDiscoveryListener();
boolean limited = false;
boolean started = discoveryAgent.startInquiry(
limited ? DiscoveryAgent.LIAC : DiscoveryAgent.GIAC,
discoveryListener);

// retrieve name of first remote device in list
RemoteDevice remoteDevice = discoveryListener.getDiscoveredDevices().get(0).getRemoteDevice();
String deviceName = remoteDevice.getFriendlyName(true);

// search for services using RFCOMM on the first remote device
UUID[] uuids = new UUID[]{new UUID(BluetoothConstants.PROTOCOL_RFCOMM)};
int transId = discoveryAgent.searchServices(null, uuids, remoteDevice,
discoveryListener);

// retrieve a connection URL using the first service record
ServiceRecord record = discoveryListener.getServiceRecords().get(transId)[0];
String url = record.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);

// open L2CAP, RFCOMM & OBEX connections
L2CAPConnection l2capConnection = (L2CAPConnection) Connector.open(url);

StreamConnection rfcommConnection = (StreamConnection) Connector.open(url);

ClientSession obexConnection = (ClientSession) Connector.open(url);
HeaderSet hsConnectReply = obexConnection.connect(null);
if (hsConnectReply.getResponseCode() != ResponseCodes.OBEX_HTTP_OK) {
// 196 = not found, 204 = precon failed, 160 = ok
LOG.error(“expected OBEX_HTTP_OK but got ” + hsConnectReply.getResponseCode());
}

Hoe service registratie werkt (dit is: opzetten van een server connectie die wacht op clients) kun je terugvinden in de Eclipse workspace.

OBEX – OBject EXchange

OBEX is ‘geleend’ van de IrDa (infrarood) wereld, vandaar dat de API een eigen package javax.obex heeft. Binnen bt werkt OBEX bovenop RFCOMM, maar OBEX ondersteunt ook IrDa en TCP/IP als transportlaag.

Ondersteunde diensten zijn o.a. FTP (phone2PC), Object Push (phone2phone – zogezegd gebruikt bij toothing, Sync, basic imaging en basic printing.

Het protocol doet denken aan HTTP. Herkenbare concepten zijn:
• Headers 12 gedefinieerde parameters, o.a. name, type en length
• Sessions op basis van connectie ID
• Content types text/vcard, x-obex/folder-listing, etc.
• Commands CONNECT, PUT, GET, SETPATH, ABORT, CREATE-EMPTY, PUT-DELETE en DISCONNECT
• Error codes OBEX_HTTP_OK, OBEX_HTTP_NOT_FOUND, etc.

Voorbeelden van uit te wisselen data zijn: vCard, vCalendar, vMessage, vNotes en platte bestanden.

De belangrijkste 4 klassen/interfaces en hun supertypes zijn:
• ClientSession > Connection
• HeaderSet
• Operation > ContentConnection > StreamConnection > OutputConnection & InputConnection > Connection
• SessionNotifier > Connection

JSR-82 implementaties & bt stacks

Bluecove is momenteel de enige actief onderhouden, bruikbare en gratis JSR-82 implementatie, maar werkt gelukkig redelijk goed. Bluecove heeft wel een bluetooth stack nodig. Voor Windows wordt widcomm aangeraden en BlueSoleil ten zeerste afgeraden – dit kan ik beamen! De bluetooth stack wordt meegeleverd met je chipset, dus kiezen kun je niet. Gelukkig bevat mijn Ordina laptop (HP Compaq 6910p) een widcomm ingebouwd. Wel eerst even een service pack van 100MB installeren! Voordat ik dat deed werd de widcomm niet herkend en ben ik met een USB bt stikkie van cygnet aan het klooien geweest. Helaas zat daar BlueSoleil bij en dat ging niet erg lekker.

Op Linux kun je gebruik maken van de BlueZ stack. Ik heb ‘m niet geprobeerd – laat me je ervaringen weten!

Een mogelijk client programma is Nokia PC Suite, die bijvoorbeeld. een remote file browser biedt. Helaas installeert Nokia zoveel dingen achter je rug om (koppelt onder andere de JAR extensie aan een Nokia application installer!) dat ik deze afraad.

Overigens werkt ook de combinatie widcomm & bluecove niet vlekkeloos. Enkele keren heb ik een JVM crash gehad, een vastlopende BT stack (een volledige herstart helpt, maar soms is killen van BTStackServer.exe voldoende), een BCC die mijn connecties afpakte (alleen een unpair/pair-actie verhelpt dit), et cetera. Als je de documentatie van bluecove mag geloven, ligt dit eerder aan de slechte staat van bluetooth stacks, danwel aan een mismatch tussen stacks en JSR-82, dan aan bluecove zelf. Toch krijg ik een beetje een déjà-vu met JMF en Java Serial: ook dit zijn Java extensies waarvoor eigenlijk nooit een goede (reference) implementatie is gekomen, en door Sun een beetje aan hun lot lijken te zijn overgelaten.

Documentatie

Er bestaat een bluetooth SIG die alle specs publiceert; onder andere die van alle profielen. Helaas moet je voor de meest informatie lid zijn, en dat kunnen alleen betalende bedrijven of universiteiten. Ik heb ze gecontacteerd voor een simpele lijst met standaard UUID nummers, en kreeg pas na lang aandringen antwoord – natuurlijk pas nadat ik mijn code af had. De online documentatie & tutorials die ik wél gevonden heb is helaas vaak slecht, oud, weinig diepgaand en lastig te vinden – ik heb hier en daar door open source code moeten snuffelen voor ontbrekende informatie (zoals voornoemde UUID nummers).

Voor mijn source code, verwijzingen naar boeken, meer technische informatie, een lijst van geregistreerde UUIDs, attributen en service classes, en extra URLs verwijs ik je naar de Ordina Wiki pagina die ik hiervoor opgezet heb.

Mocht je zelf ook aan de gang gaan met Java bluetooth, dan hoor ik graag van je terug wat je resultaten zijn. Codebijdragen zijn natuurlijk welkom op de Wiki!

Hedzer Westra

Hedzer Westra




Java 6 undocumented change: @Override

Door: Jan-Kees van Andel, 15 October 2008

Het valt me op projecten al een tijdje op dat collega’s compilatiefouten krijgen op @Override. Deze fouten kreeg ik zelf nooit. Gisteren tijdens de JSF cursus gebeurde het weer. Weer twee mensen die tegen deze fout aan liepen. Bij de rest van de groep ging het goed, maar bij deze twee niet, ondanks dat ze dezelfde stappen doorliepen als de rest.

In de code kwamen een stuk of 20 compile errors naar voren, allemaal gerelateerd aan @Override. De melding was:

The method [METHODNAME] of type [CLASSNAME] must override a superclass method

Het probleem is een aanpassing die in Java 6 is gemaakt. In Java 5 mag deze annotation alleen op een method staan die een method uit een klasse override. Of de method in de superclass abstract is en of de superclass zelf abstract is, maakt niet uit. Wat wel uitmaakt in Java 5, is dat een method die een interface method implementeert, niet met @Override geannoteerd mag zijn.

Dit mag dus niet in Java 5.

interface MyInterface {
    void doSomething();
}
 
class MyImplementation implements MyInterface {
    // Error in Java 5: The method doSomething() of type MyImplementation must override a superclass method
    @Override
    void doSomething() {}
}

Het vervelende was dat Eclipse die annotaties neer had gezet omdat mijn Compiler Compliance Level op 6 stond en ik een Java 6 compiler gebruikte, maar niet iedere cursist gebruikte Java 6.

Leuk detail: We hebben het hier over een (per ongeluk) niet gedocumenteerde feature van Java 6. Zie Peter Ahé’s weblog.
http://blogs.sun.com/ahe/entry/override
http://blogs.sun.com/ahe/entry/override_snafu




[Closures] Introductie

Door: Jan-Kees van Andel, 29 August 2008

Closures, één van de meest bediscussieerde onderwerpen van de afgelopen paar jaren in de Java wereld. Deels een hype, deels bieden closures meerwaarde voor Java development. Ik ga niet meedoen met alle discussies waarom ze (niet) gewenst zijn, maar het is een feit dat het mogelijkheden biedt qua API design. In dit stuk hoop ik a) de mystieken van closures iets duidelijker te maken en b) wat inzicht te geven in de huidige closures oorlog bij Java.

Closures

Wat is een closure? WikiPedia (http://en.wikipedia.org/wiki/Closure_(computer_science)) heeft de volgende nietszeggende definitie: “In computer science, a closure is a function that is evaluated in an environment containing one or more bound variables. When called, the function can access these variables.”. Hoewel deze definitie klopt, is hij niet echt duidelijk. Waar komt het op neer?

Bij een closure wordt een functie uitgevoerd in een andere context dan waarin deze wordt gecreëerd. Deze zogenaamde “function context” wordt bij het creëeren van de functie bij de referentie naar de functie opgeslagen. Dit betekent dat als iemand deze referentie te pakken krijgt en de functie aanroept, de variabelen uit de context waarin de functie gemaakt is, beschikbaar zijn.

Voordat ik concreter wordt, wil ik nog even aangevan dat closures niet nieuw zijn. In vrijwel elke mainstream taal zit een soortgelijke constructie. Ook in Java, namelijk met anonymous inner classes, maar andere talen, zoals Smalltalk, JavaScript en Ruby bieden veel meer flexibiliteit.

Nog steeds nietszeggend? Dan maar een codevoorbeeld (JavaScript):

<html>
  <head>
    <script type="text/javascript">
      function initPage() {
        var counter = 0;
        window.onclick = function() {
          alert("Click #" + ++counter);
        };
      }
      window.onload = initPage;
    </script>
  </head>
  <body></body>
</html>

Wat gebeurt hier? Om te beginnen zijn functies in JavaScript first class objecten, in tegenstelling tot in Java, waarin methoden altijd onderdeel van een object zijn. Dit betekent onder andere dat je naar een functie kunt refereren, of deze referentie in een variabele kunt stoppen om deze daarna rond te gooien. Dit kun je zien aan de regel “window.onclick = …” waarin een object van het type function aan de onclick property van het window object toegekend wordt.

Wat (hopelijk) ook opvalt is dat er een function in een andere function gedeclareerd wordt. In dit voorbeeld spreken we van een anonymous inner function. Dit is een belangrijk detail, aangezien dit voor de JavaScript interpreter betekent dat er een closure gecreeerd moet worden. Wat betekent dit?

Kort gezegd betekent een closure dat bij de referentie naar de anonymous inner function, alle variabelen uit de enclosing scope bewaard blijven. Vandaar de naam closure. Deze variabelen vormen de function context welke vanaf nu een deel van de functie geworden. Elke keer als je deze functie aanroept, zijn de variabelen in de function context beschikbaar.

Wat is het nut dan van deze closure? Nou, de variabele “counter” heeft een function scope en verdwijnt op het moment dat de functie eindigt. Bij een event handler zoals in bovenstaand voorbeeld betekent dit dus dat de variabele op het moment van de klik niet bruikbaar is, aangezien het click-event niet in dezelfde stack plaatsvindt. Dit betekent dus dat je zonder closures een globale variabele nodig hebt om een simpel tellertje te maken. Met closures heb je deze beperking niet, want de variabele counter blijft bewaard en de ++ operator werkt de variabele in de function context bij, zodat het ook een echte teller is.

Conclusie

Closures bieden dus de mogelijkheid om stack confined variabelen te bewaren over meerdere stacks heen en zijn daardoor dus zeer geschikt voor event handlers. Dat is ook de reden dat ze in JavaScript veel gebruikt worden. JavaScript wordt voornamelijk gebruikt voor het koppelen van event handlers aan buttons etc. Daarnaast biedt het een mooie compacte syntax.

Volgende keer kijken we naar de Java closures initiatieven.




Request in LoginModule

Door: Jan-Kees van Andel, 28 July 2008

Je zult het misschien weleens gewild hebben, vanuit je JAAS LoginModule toegang hebben tot je HttpServletRequest. Helaas biedt JAAS hier geen ondersteuning voor.
Je JAAS LoginModule wordt namelijk door JAAS aangeroepen en je krijgt alleen een Subject, een CallbackHandler implementatie en nog een zootje parameters mee. Maar dus geen servlet objecten.

JACC

Voor deze beperking heeft Sun een oplossing (bestaal al sinds 2003), namelijk JACC. JACC staat voor Java Authorization Contract for Containers en is een specificatie die eigenlijk voor Container leveranciers bedoeld is. Echter, je kunt het als applicatie ontwikkelaar ook gewoon gebruiken.

JACC heeft namelijk de klasse PolicyContext en in deze klasse zit een methode getContext(String).

getContext accepteert de volgende Strings:

  • javax.security.auth.Subject.container
    Geeft het huidige Subject terug.
  • javax.xml.soap.SOAPMessage
    Geeft de SOAPMessage terug bij een EJB Web Service Endpoint (alleen EJB).
  • javax.servlet.http.HttpServletRequest
    Geeft het request terug.
  • javax.ejb.EnterpriseBean
    Idem voor de EJB.
  • javax.ejb.arguments
    Geeft een array terug met de parameters waarmee de EJB aangeroepen wordt. (alleen EJB)

In de spec staat niets over Java EE 5 of EJB 3.0, maar aangezien aan de onderliggende architectuur niets veranderd is, zal het ook op een Java EE 5 container wel werken.

Het HttpServletRequest

Hieronder staat een LoginModule die de HttpSession nodig heeft.

import javax.security.jacc.PolicyContext;
import javax.security.jacc.PolicyContextException;

// Other imports skipped

public class CustomLoginModule implements LoginModule {

    private Subject subject;
    private CallbackHandler callbackHandler;
    private Object object;

    public void initialize(Subject subject,
                           CallbackHandler callbackHandler,
                           Map sharedState,
                           Map options) {

        this.subject = subject;
        this.callbackHandler = callbackHandler;

        // Get an object from the HttpSession.

        try {
            HttpServletRequest req = (HttpServletRequest) PolicyContext.getContext("javax.servlet.http.HttpServletRequest");
            object = req.getSession().getAttribute("someAttribute");
        } catch (PolicyContextException e) {
            LOGGER.error("Unable to retrieve object from user session:", e);
            object = null;
        }
    }

    public boolean login() throws LoginException {
        // Do some login stuff and use the object from the session
    }

    public boolean commit() throws LoginException {}

    public boolean abort() throws LoginException {}

    public boolean logout() throws LoginException {}
}

Zoals je ziet, kun je via PolicyContext.getContext gemakkelijk aan het request komen. En als je een request hebt, is de sessie niet ver meer.

Met deze klasse wordt het dus stukken gemakkelijker om context specifieke zaken te gebruiken bij het inloggen.




JSF 2.0 EDR: Publish/Subscribe Event System

Door: Jan-Kees van Andel, 13 July 2008

In de vorige post heb ik het over het nieuwe resource management systeem van JSF 2.0 gehad. Dit keer wil ik met een voorbeeldje laten zien wat je kunt met het nieuwe Publish/Subscribe Event System.

Publish/Subscribe

Publish/Subscribe is een algemeen toegepast mechanisme om event driven applicaties te maken. Het werkt zo: Een publisher is de zender van een bericht. Deze verzendt een bericht naar de runtime die Publish/Subscribe verzorgt, zoals middleware of een framework. Op basis van bijvoorbeeld configuratie is bekend welke ontvangers geïnteresseerd zijn in dit bericht en het bericht wordt naar al deze ontvangers verstuurd.

Eigenschappen van Publish/Subscribe zijn:

  • Ontkoppeling van zender en ontvanger; het enige dat ze verbindt, is het bericht,
  • Een zender kan een bericht naar meerdere ontvangers sturen,
  • Een ontvanger kan van meerdere zenders een bericht ontvangen.

Typische Publish/Subscribe voorbeelden zijn een radiostation en RSS feeds. Publish/subscribe in GUI’s wordt doorgaans geimplementeerd middels het Observer pattern.

Laten we eerst kijken hoe JSF 2.0 dit model wil implementeren.

JSF 1.x events

De huidige versie van JSF bevat al een event mechanisme waarmee objecten/componenten events kunnen versturen en ontvangen. Een voorbeeld hiervan is het UICommand component, waarvan <h:commandLink /> en <h:commandButton /> gebruikmaken. Dit component kan ActionEvents versturen, wat ervoor zorgt dat bijvoorbeeld een action method in een managed bean uitgevoerd wordt.

Dit hele systeem draait om de methode queueEvent, gedefinieerd in UIComponent. Met deze methode worden events verstuurd die door de lifecycle op het juiste moment naar de juiste listener verstuurd worden.

Daarnaast heb je PhaseEvents, die aan het begin en aan het einde van elke fase in de lifecycle verstuurd worden en door PhaseListeners afgevangen worden.

Ten slotte definieert de JSF 1.x spec ValueChangeEvents, die afgevuurd worden als de nieuwe waarde (submittedValue) van een UIInput anders is dan de waarde tijdens het renderen van de view.

Naast de standaard events is het mogelijk om eigen events te definiëren, bijvoorbeeld als je een AJAX component library ontwikkelt.

JSF 2.0

In JSF 2.0 wordt een nieuw soort event geïntroduceerd, namelijk het SystemEvent. Dit event wordt gebruikt om op specifieke momenten in te prikken in de JSF applicatie. Op het moment zijn er 4 soorten SystemEvents gedefinieerd, namelijk:

  • AfterAddToParentEvent
    Geeft aan dat het component (de source parameter) is toegevoegd aan de component tree.
  • BeforeRenderEvent
    Geeft aan dat het component (de source parameter) straks gerenderd wordt. Dit is het moment om last minute aanpassingen te doen.
  • ViewMapCreatedEvent
    De view scope is aangemaakt. Zie View scope.
  • ViewMapDestroyedEvent
    De view scope is verwijderd. Zie View scope.

De spec is echter nog niet definitief, dus er kunnen nog events bijkomen. Daarnaast kun je zelf events definiëren.

Deze events maken het mogelijk om tweaks te doen aan de lifecycle van JSF, zonder terug te hoeven vallen op low-level zelfbouwoplossingen, zoals eigen PhaseListeners. Wie zelf weleens een AJAX component geschreven heeft met een PhaseListener (om het AJAX request af te vangen of voor resource requests), weet hoe onhandig het werkt en hoe foutgevoelig het is.

Bovendien wordt integratie van component libraries hiermee stukken makkelijker, aangezien ze onder water allemaal van dezelfde mechanismen gebruik maken. Nu nog hopen dat het voldoende flexibiliteit biedt voor libraries zoals Trinidad en Ajax4jsf, zodat ze hun eigen (Filter/RenderKit/ContextListener) oplossingen te migreren naar de officiële manier.

Eerlijk gezegd valt het systeem me tot nu toe nog wat tegen, voornamelijk het registreren van event listeners is niet echt handig. De voorbeeldapplicatie die met de JSF 2.0 EDR meegeleverd wordt, bevat een hack in de meegeleverde Facelets versie (de methode apply in ValidateHandler is non-final gemaakt) die het mogelijk maakt om de listener te registreren. Zonder een dergelijke hack kun je niet fatsoenlijk listeners registreren. Ik hoop dat ze een soort configuratie systeem toevoegen, want nu moet je weer terugvallen op PhaseListeners. :(

View scope

In JSF 2.0 is een nieuwe scope gedefinieerd, naast de bestaande scopes (request, session, application), namelijk de View scope. De spec zegt nog niets over de view scope, maar conceptueel is de view scope al wel bekend.

https://javaserverfaces-spec-public.dev.java.net/issues/show_bug.cgi?id=290

De view scope zit tussen request en sessie in qua lifetime. De view scope begint op het moment dat een UIViewRoot instantie aangemaakt wordt en eindigt als een nieuwe UIViewRoot instantie actief wordt gemaakt via FacesContext.setViewRoot.

Concreet betekent dit dat zolang je op dezelfde pagina blijft (bijvoorbeeld een invoerscherm, waarin je – voordat je op “Save” drukt – nog wat extra acties doet), de data in de view scope in leven blijft. Op het moment dat je naar de volgende pagina (bijvoorbeeld van detail terug naar master) navigeert, wordt de view scope gereset.

Hoe werkt het?

Om dit alles mogelijk te maken zijn er een paar aanpassingen gedaan aan de API.

SystemEvent (nieuw)
Dit is de base klasse waarvan alle SystemEvents moeten overerven.

SystemEventListener (nieuw)
Dit is een nieuwe interface, die aangeeft dat de implementerende klasse als listener kan dienen voor SystemEvents. Er zijn twee methoden gedefinieerd, namelijk isListenerForSource en processEvent. processEvent spreekt voor zich, die wordt aangeroepen met als parameter de SystemEvent instantie. isListenerForSource wordt gebruikt om de calls van processEvent te beperken. Bijvoorbeeld tot events die gegooid zijn door instanties van een bepaald type. Je kunt een SystemEventListener dus configureren om alleen events van bepaalde componenten te verwerken.

ComponentSystemEventListener (nieuw)
Deze interface doet hetzelfde als SystemEventListener, maar bevat geen methode isListenerForSource, aangezien de koppeling met een bepaald type component al voor een impliciete variant van deze methode zorgt.

UIComponent (toevoegingen)
Hieraan zijn nieuwe methoden toegevoegd, namelijk subscribeToEvent, unsubscribeFromEvent en getListenersForEventClass.

public void subscribeToEvent(FacesContext context,
                             Class<? extends SystemEvent> eventClass,
                             ComponentSystemEventListener componentListener)

Met subscribeToEvent kun je componentListener aan deze component instantie koppelen die alle events afvangt die van het type eventClass zijn.

Met unsubscribeToEvent doe je precies het tegenovergestelde, namelijk de listener weer verwijderen.

getListenersForEventClass returned een lijst met alle listeners die events van het opgegeven type afvangen.

Application (toevoegingen)
Hieraan zijn ook een aantal methoden toegevoegd, namelijk subscribeToEvent, unsubscribeFromEvent en publishEvent. De eerste twee zijn volgens mij wel duidelijk, maar nu zijn de event handlers niet gekoppeld aan een specifieke component instantie. Met publishEvent kun je een event opgooien die dan door de JSF runtime verwerkt wordt.

@ListenerFor (nieuw)
Ten slotte is een annotation gemaakt, genaamd ListenerFor. Klassen die hiermee geannoteerd zijn, worden als listener geregistreerd. Dit gebeurt op dezelfde manier als hoe de subscribeToEvent methoden op Application en UIComponent werken. Dit is de verantwoordelijkheid van JSF.

Als je de annotation gebruikt heb je minder last van het feit dat er geen fatsoenlijk configuratiemechanisme is.

Voorbeeld

Iedereen (die met JSF gewerkt heeft) heeft het weleens gehad. Er treedt een validatiefout op, maar je hebt het niet door, omdat er geen <h:messages /> tag op de pagina ligt. Er vervelend. In dit voorbeeld laat ik een oplossing hiervoor zien met behulp van het beschreven event mechanisme.

We gaan door met de code uit het vorige voorbeeld. Ik beschrijf hier alleen de onderdelen die betrekking hebben op dit onderwerp.

Het idee is dat we op het moment dat we de view gaan renderen, kijken of er al een <h:messages /> tag op de pagina ligt. Als dit niet het geval is, voegen we deze programmatisch toe.

Een extra detail, dit mag alleen zichtbaar zijn tijdens development, daarom moeten we de volgende code toevoegen aan web.xml:

web.xml

<context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
</context-param>

Om te beginnen, zorgen we dat er geen <h:messages /> tag in de pagina ligt. Zo forceren we dat ons mechanisme afgaat. Zie de volgende pagina.

homepage.xhtml

<html ... >
<h:head>
  <title>JSF2 test</title>
</h:head>
<h:body>
  <p>Hello, you are logged in. </p>
  <h:form>
    <h:commandButton action="#{loginBean.addMessage}" value="Click" />
    <hr />
    <h:commandLink action="loginPage">Back to login page</h:commandLink>
  </h:form>
</h:body>
</html>

Dan maken we de event listener klasse, die ervoor zorgt dat een <h:messages /> tag aan de pagina wordt toegevoegd. In dit voorbeeld heb ik er zelfs een custom component van gemaakt, zodat je tijdens development goed kunt zien dat dit component automatisch toegevoegd is. Dat doen we in de volgende klasse.

AddDefaultMessagesListener.java

public class AddDefaultMessagesListener implements SystemEventListener {

    public boolean isListenerForSource(Object source) {
        return source instanceof UIViewRoot;
    }

    public void processEvent(SystemEvent evt) throws AbortProcessingException {
        if (!(evt instanceof BeforeRenderEvent)) {
            String klazz = (evt != null) ? evt.getClass().getName() : "null";
            throw new AssertionError("Event '"+evt+"' is not of type BeforeRenderEvent, but of type '"+klazz+"'");
        }

        FacesContext fc = FacesContext.getCurrentInstance();
        if (fc.getApplication().getProjectStage().equals(ProjectStage.Development)) {
            if (!hasMessagesComponent(fc, fc.getViewRoot())) {
                String viewId = fc.getViewRoot().getViewId();
                System.out.println("No <h:messages /> tag found in view '"+viewId+"'. ");
                addMessagesComponentToPage(fc, fc.getViewRoot());
            }
        }
    }

    private void addMessagesComponentToPage(FacesContext fc, UIViewRoot viewRoot) {
        String viewId = fc.getViewRoot().getViewId();
        UIForm form = findFirstForm(fc, viewRoot);
        if (form != null) {
            System.out.println("Adding default <h:messages /> tag to view '"+viewId+"' in the first form on the page. ");
            form.getChildren().add(0, new DummyUIMessages());
        } else {
            System.out.println("No form found while trying to add default <h:messages /> tag to view '"+viewId+"'. No <h:messages /> added. ");
        }
    }

    private boolean hasMessagesComponent(FacesContext fc, UIComponent comp) {
        if (comp instanceof UIMessages) {
            return true;
        }
        for (UIComponent child : comp.getChildren()) {
            if (hasMessagesComponent(fc, child)) {
                return true;
            }
        }
        return false;
    }

    private UIForm findFirstForm(FacesContext fc, UIComponent comp) {
        if (comp instanceof UIForm) {
            return (UIForm) comp;
        }
        for (UIComponent child : comp.getChildren()) {
            UIForm form = findFirstForm(fc, child);
            if (form != null) {
                return form;
            }
        }
        return null;
    }
}

Deze code zal vast niet optimaal zijn, al is het maar dat er naar de std out gelogged wordt. Ook zullen de zoekfuncties niet bijzonder efficiënt zijn, al weet ik zo snel geen snellere manier dan een vorm van caching en dat is weer erg fragiel aangezien de component tree nogal dynamisch is.

Deze listener wordt nog niet aangeroepen, want hij is nog niet geregistreerd. Dat doen we nu.

ConfigPhaseListener.java

public class ConfigPhaseListener implements PhaseListener {

    private static final Object LOCK = new Object();

    private static boolean initialized = false;

    //@BadCode(type="reallyBadCodeDoNotUseInProduction")
    public ConfigPhaseListener() {
        synchronized (LOCK) {
            if (!initialized) {
                AddDefaultMessagesListener listener = new AddDefaultMessagesListener();
                FacesContext fc = FacesContext.getCurrentInstance();
                fc.getApplication().subscribeToEvent(BeforeRenderEvent.class, listener);

                initialized = true;
            }
        }
    }

    public void afterPhase(PhaseEvent event) {}

    public void beforePhase(PhaseEvent event) {}

    public PhaseId getPhaseId() {
        return PhaseId.RESTORE_VIEW;
    }
}

Deze code is een beetje eng. Ten eerste wordt er in de constructor gesynchroniseerd. Dit is sowieso alleen maar mogelijk omdat het op een static variabele gebeurt, want in de constructor synchroniseren op this is gevaarlijk, aangezien this naar een algemeen beschikbaar object wijst. Ook staat er een static boolean initialized die in de constructor op true gezet wordt. De reden dat die er staat, is dat in de JSF spec niets gezegd wordt over de lifecycle van PhaseListeners.
Deze twee constructies (syncen en initalized boolean) zorgen er samen voor dat er niet meerdere instanties van de listener geregistreerd worden. Meerdere listeners zorgen namelijk voor dubbele output in de HTML pagina en dat willen we niet.

Note: Dit is absoluut niet production ready! Ik heb bijvoorbeeld geen rekening gehouden met geclusterde deployments.

We hebben nu de SystemEventListener en een PhaseListener om deze te registreren, ter volledigheid zal ik de faces-config nog even laten zien.

faces-config.xml

<lifecycle>
    <phase-listener>nl.ordina.jsf2.bean.ConfigPhaseListener</phase-listener>
</lifecycle>

Ten slotte mijn eigen UIMessages implementatie, die duidelijk laat zien dat je een fout gemaakt hebt.

public class DummyUIMessages extends UIMessages {

    public void encodeEnd(FacesContext context) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        assert(writer != null);

        writer.startElement("div", this);
        writer.writeAttribute("style", "border: 1px solid #FF0000", null);
        writer.startElement("h1", this);
        writer.writeAttribute("style", "color: #FF0000", null);
        writer.write("Automatically inserted messages tag, for development purposes only. ");
        writer.write("");
        writer.write("Add a <h:messages /> tag to remove this crap. ");
        writer.endElement("h1");

        super.encodeEnd(context);

        writer.endElement("div");
    }
}

Je kunt waarschijnlijk wel zien wat deze code doet. Er wordt een dikke vette grote rode tekst getoond met een waarschuwing. Daaronder staan de messages.

En om het te bewijzen, hier een screenshot.

DummyUIMessages screenshot

Conclusie

Het nieuwe event mechanisme staat nog in zijn kinderschoenen, maar er staat wel een vrij goede basis waarmee je op zijn minst aan de gang kunt. Ik heb momenteel maar een paar dingen aan te merken, namelijk:

  • Ik wil nog meer soorten events, bijvoorbeeld events voor Exception handling, of events die veroorzaakt worden door specifieke lifecycle events, zoals validatiefouten waardoor de lifecycle kortsluit.
  • Ook zijn de event listeners nog niet echt goed te configureren. De API is er, maar waar roep je die methoden aan? Een ServletContextListener is geen optie, want die valt buiten de JSF FacesContext. Ik heb voor bovenstaand voorbeeld een PhaseListener gebruikt, maar dat is ook niet ideaal, aangezien een PhaseListener bedoeld is om faseovergangen te herkennen en niet gemaakt is voor initialisatie van je framework. JSF biedt hier echter geen hooks voor. Bovendien geeft het overhead. Deze overhead valt op zich wel mee, aangezien de logica in de constructor staat, maar dat is ook een hack, want de spec zegt niets over de lifecycle van PhaseListeners. Dat is ook de reden van deze overdreven defensieve constructie. Het is simpelweg een hack, maar hij werkt wel en deze extra checks maken hem ook portable over JSF implementaties.
  • Bovendien zou een meer declaratieve manier van configureren prettig zijn. Er zit een annotation bij, maar ik geef de voorkeur aan een XML config file, zoals een paar extra tags in faces-config.xml. Misschien kunnen ze bij JSF ook overstappen op namespaces zoals bij Spring.

Download sources

Klik hier voor de voorbeeldcode. Er zit ook een simpel autocomplete component bij dat gebruikmaakt van het nieuwe event mechanisme om AJAX requests af te handelen.

Autocomplete component screenshot

Note: ook dit autocomplete component is niet production-ready! Ik heb geen rekening gehouden met performance, security, robuuste code, JSF implementatie portability, Java versie, cross browser issues, client side memory usage, etc. Ook zul je de Prototype.js dependency waarschijnlijk niet willen.

Bronnen

Weblog van Ryan Lubke, waar nieuwe features van JSF 2.0 beschreven worden.
http://blogs.sun.com/rlubke/entry/jsf_2_0_new_feature1

De download pagina van de JSF 2.0 specificatie.
http://jcp.org/aboutJava/communityprocess/edr/jsr314/index.html